Skip to content

Governance checks

Every check a governance policy can require, generated from mapi_spec.governance.GOVERNANCE_CHECKS (the catalog behind GET /v1/catalog/governance-checks, mapi governance checks and the web app's Check catalog). Each entry starts with its plain-language About card. Can you trust this model? explains the null tests in plain words and in statistics.

A policy may set a check's severity (blocking, warning, info), its own warn and fail limits (never looser than the order below allows) and its parameters. A check whose requirement does not hold for a model is reported as skipped with the reason; one that cannot be computed is error, which fails a blocking check.

Check Title Measures Default limits Needs
baseline.beats_naive Better than simple benchmarks relative_skill pass at ≥ 0.05, warn down to 0, fail below holdout, time_series
convergence The fit is complete and stable r_hat_max pass at ≤ 1.01, warn up to 1.05, fail above –
data.quality The data is good enough data_issue_count pass at ≤ 0, warn up to 2, fail above –
holdout.accuracy Accurate on test data holdout_error pass at ≤ 0.15, warn up to 0.3, fail above holdout
null.noise_regressors No effect from false drivers noise_effect_share pass at ≤ 0.02, warn up to 0.05, fail above –
null.permutation_target Better than shuffled data permutation_p_value pass at ≤ 0.05, warn up to 0.1, fail above holdout
null.placebo_time_shift No effect after a time shift placebo_effect_ratio pass at ≤ 0.25, warn up to 0.5, fail above time_series
plausibility.signs Effects have the correct direction wrong_sign_share pass at ≤ 0.1, warn up to 0.25, fail above –
recovery.simulated Finds known values recovery_coverage pass within 0.1 of 0.9, warn within 0.25, fail further away –
residuals.whiteness No pattern in the errors ljung_box_p_value pass at ≥ 0.05, warn down to 0.01, fail below residuals, time_series
sensitivity.prior The results come from the data prior_sensitivity pass at ≤ 0.2, warn up to 0.5, fail above bayesian
stability.rolling_params Estimates are stable over time param_variation pass at ≤ 0.25, warn up to 0.5, fail above time_series

baseline.beats_naive - Better than simple benchmarks

Compares the error of the model on the test periods with very simple forecasts, for example 'same as last week' or 'same as last year'. The model must be better.

Difficulty: beginner. Typical runtime: seconds.

Use it when: Each forecast model.

Avoid it when: Models that do not forecast over time.

It needs: The test periods of the model and the history before them.

It assumes: The check scores the benchmarks on the same periods.

You get: The skill against the best benchmark (1 - model error / benchmark error).

Example: The error of the model is 30% lower than 'same as last year': skill 0.30, pass.

Related: evaluate.baseline, holdout.accuracy.

Expert detail

Relative skill 1 - L(model)/min_b L(b) on the holdout or backtest folds; MASE-style scaling by the in-sample seasonal-naive error is reported as well. References: Hyndman & Koehler (2006), Another look at measures of forecast accuracy.

Category baseline
Metric relative_skill
Default limits pass at ≥ 0.05, warn down to 0, fail below
Default severity blocking
Applies to every model family
Skipped when the model has no test period (add a data split and fit the model again); the data of the model has no time dimension
Version 1.0.0

Parameters (params in a policy)

Field Type Default Description
methods list of naive or seasonal_naive or mean or drift ["naive", "seasonal_naive"]
metric string mae

convergence - The fit is complete and stable

Makes sure that the fit procedure found a stable answer. If you fit the model again, you get the same result.

Difficulty: beginner. Typical runtime: seconds.

Use it when: Every model.

Avoid it when: Models that you fit outside the platform, without diagnostics.

It needs: The diagnostics of the fit.

It assumes: The fit recorded its diagnostics.

You get: R-hat, effective draws and divergences (Bayesian) or the optimiser status.

Example: All chains agree (R-hat 1.003): pass.

Related: recovery.simulated.

Expert detail

Bayesian: max split R-hat (rank-normalised), with ESS and divergences as evidence; frequentist: optimiser convergence flag (value 1.0 when converged, inf otherwise).

Category convergence
Metric r_hat_max
Default limits pass at ≤ 1.01, warn up to 1.05, fail above
Default severity blocking
Applies to every model family
Skipped when never
Version 1.0.0

data.quality - The data is good enough

Makes sure that the data is good before you trust the model. The data must have few gaps, enough history, no constant columns and no impossible values.

Difficulty: beginner. Typical runtime: seconds.

Use it when: Every model.

Avoid it when: Synthetic data that you made yourself.

It needs: The dataset that the model fit used.

It assumes: The limits are correct for your data.

You get: The data problems that the check found, each in plain words.

Example: No gaps, 3 years of history, each driver changes: pass.

Related: convergence.

Category data
Metric data_issue_count
Default limits pass at ≤ 0, warn up to 2, fail above
Default severity blocking
Applies to every model family
Skipped when never
Version 1.0.0

Parameters (params in a policy)

Field Type Default Description
max_missing number 0.05 ≥ 0, ≤ 1
min_history_periods integer 52 ≥ 1

holdout.accuracy - Accurate on test data

Makes sure that the error of the model on periods that the fit did not use stays below the limit of your organisation.

Difficulty: beginner. Typical runtime: seconds.

Use it when: Each model that you use for forecasts or what-ifs.

Avoid it when: Models without a test period.

It needs: A test period or a backtest.

It assumes: The test periods are similar to future use.

You get: The selected error metric on the test data.

Example: MAPE 9% against a limit of 15%: pass.

Related: baseline.beats_naive, evaluate.metrics.

Category holdout
Metric holdout_error
Default limits pass at ≤ 0.15, warn up to 0.3, fail above
Default severity blocking
Applies to every model family
Skipped when the model has no test period (add a data split and fit the model again)
Version 1.0.0

Parameters (params in a policy)

Field Type Default Description
metric mape or smape or mase or rmse or mae mape

null.noise_regressors - No effect from false drivers

Adds some false drivers that are only noise and fits the model again. A model that you can trust gives them almost no effect.

Difficulty: intermediate. Typical runtime: long.

Use it when: Marketing mix models and other models that divide a result between drivers.

Avoid it when: Forecast models that have no drivers.

It needs: The data and the settings of the model.

It assumes: The false drivers look like real drivers, but they have no signal.

You get: The largest share of the effect of the real drivers that goes to a false driver; If a false driver is clearly above the limit.

Example: Three false channels get 0.4%, 0.1% and 0.9% of the media effect: pass.

Related: null.permutation_target, plausibility.signs.

Expert detail

Placebo regressors drawn as Gaussian noise or as a driver shuffled in blocks, with the model's default prior (never a real channel's prior or calibration); the statistic is the largest placebo share of the real drivers' total effect (by ablation through the score path). Bayesian: flagged when P(share > warn limit) >= 0.9 over the posterior (practical equivalence, valid for sign-constrained effects); frequentist: placebo p-values with Holm's correction.

Category null_test
Metric noise_effect_share
Default limits pass at ≤ 0.02, warn up to 0.05, fail above
Default severity blocking
Applies to every model family
Skipped when never
Version 1.0.0

Parameters (params in a policy)

Field Type Default Description
n_noise integer 3 ≥ 1, ≤ 10
kind gaussian or shuffled_driver shuffled_driver
seed integer 0 ≥ 0

null.permutation_target - Better than shuffled data

Fits the model again on copies of the data where the drivers have a random time order. A real model must be clearly better on the true data than on the shuffled copies.

Difficulty: intermediate. Typical runtime: long.

Use it when: Each model whose accuracy you use to make decisions.

Avoid it when: Models without a test period (there is nothing to compare).

It needs: The data and the settings of the model, and a test period.

It assumes: A random order of the drivers removes each real link between the drivers and the result; The trend, the season and the result stay as they are; For a model without drivers, the check shuffles the ups and downs of the result.

You get: The fraction of shuffled fits that were as good as the real fit (a p-value); The distribution of the shuffled scores.

Example: The real model forecasts better than all 19 shuffled fits: p = 0.05, pass.

Related: null.noise_regressors, baseline.beats_naive.

Expert detail

Permutation (randomisation) test on a scale-free statistic: holdout skill 1 - L(model) / L(null forecast) on each dataset (the null forecast is the last training value of an integrated series, else its mean). Null: models with drivers keep the result and get their drivers block-permuted jointly (so trend, season and controls cannot pass on their own; a model that ignores its drivers is tested as one without); models without drivers get the d-th differences of the result permuted i.i.d. and re-integrated from the original start (d from the ARIMA order or ADF/KPSS), which keeps the integration order and the increments' distribution but destroys predictable dynamics. Without a time order the result is permuted across rows and the statistic is the holdout error. p = (1 + #{perm >= real}) / (K + 1). References: Ojala & Garriga (2010), Permutation tests for studying classifier performance.

Category null_test
Metric permutation_p_value
Default limits pass at ≤ 0.05, warn up to 0.1, fail above
Default severity blocking
Applies to every model family
Skipped when the model has no test period (add a data split and fit the model again)
Version 1.0.0

Parameters (params in a policy)

Field Type Default Description
n_permutations integer 20 ≥ 5, ≤ 200
metric rmse or mae or mape or smape or crps rmse
block integer – Periods of the drivers shuffled together (default: a twentieth of the series) (≥ 1, ≤ 520)

null.placebo_time_shift - No effect after a time shift

Moves the drivers some months away from their real dates and fits the model again. The effects must become much smaller, or else the model finds time patterns, not real effects.

Difficulty: expert. Typical runtime: long.

Use it when: Models that divide results between drivers over time (marketing mix, regressions).

Avoid it when: Very short series (after the shift, the overlap is too small).

It needs: The data and the settings of the model over time.

It assumes: A real effect has a connection to the date of the driver.

You get: The placebo effect as a share of the real effect, for each shift.

Example: A shift of the spend by half a year keeps only 8% of the effect: pass.

Related: null.noise_regressors.

Expert detail

Time-shift placebo (leads/lags beyond any plausible carry-over); ratio of the placebo total effect to the real one, maximised over shifts.

Category null_test
Metric placebo_effect_ratio
Default limits pass at ≤ 0.25, warn up to 0.5, fail above
Default severity blocking
Applies to every model family
Skipped when the data of the model has no time dimension
Version 1.0.0

Parameters (params in a policy)

Field Type Default Description
shifts list of integer [-26, 26] min items 1, max items 6

plausibility.signs - Effects have the correct direction

Makes sure that each effect has the direction that business sense expects. For example, advertising must not decrease sales, and higher prices must not increase demand.

Difficulty: beginner. Typical runtime: seconds.

Use it when: Each model with effects that you can explain.

Avoid it when: Models that only forecast.

It needs: The estimates of the model and the expected directions.

It assumes: The expected directions are correct.

You get: The share of the estimate (or of the uncertainty) on the wrong side, for each effect.

Example: The effect of radio is negative with a probability of 40%: fail.

Related: null.noise_regressors, test.vif.

Category plausibility
Metric wrong_sign_share
Default limits pass at ≤ 0.1, warn up to 0.25, fail above
Default severity blocking
Applies to every model family
Skipped when never
Version 1.0.0

Parameters (params in a policy)

Field Type Default Description
expected map of positive or negative – Parameter -> expected sign (default: media positive)

recovery.simulated - Finds known values

Simulates data from the model with known true values and fits the model again. Then it makes sure that the 90% ranges of the model contain the true values about 90% of the time.

Difficulty: expert. Typical runtime: long.

Use it when: New models, or models with many changes; Before you trust a complex Bayesian model.

Avoid it when: Models that cannot simulate data.

It needs: The settings of the model and the shape of the data.

It assumes: The simulated data is similar to the real data.

You get: How often the 90% ranges contained the true values.

Example: The true values were in the 90% ranges 88% of the time: pass.

Related: sensitivity.prior, convergence.

Expert detail

Simulation-based calibration lite: N simulate-fit cycles; coverage of central 90% intervals and rank uniformity per parameter. References: Talts et al. (2018), Validating Bayesian inference algorithms with simulation-based calibration.

Category recovery
Metric recovery_coverage
Default limits pass within 0.1 of 0.9, warn within 0.25, fail further away
Default severity blocking
Applies to every model family
Skipped when never
Version 1.0.0

Parameters (params in a policy)

Field Type Default Description
n_sims integer 20 ≥ 5, ≤ 100
params list of string –

residuals.whiteness - No pattern in the errors

Makes sure that the errors of the model look like random noise over time. If a pattern stays in the errors, the model does not use all of the information in the data.

Difficulty: intermediate. Typical runtime: seconds.

Use it when: Time-series and regression models.

Avoid it when: Models without residuals.

It needs: The in-sample errors of the model.

It assumes: The time steps are regular.

You get: The Ljung-Box p-value and the autocorrelations of the errors.

Example: Ljung-Box p = 0.38 at 10 lags: pass.

Related: test.ljung_box, evaluate.residuals.

Expert detail

Ljung-Box Q on residuals (model_df adjusted for ARMA terms); the minimum p-value over series for panels.

Category residuals
Metric ljung_box_p_value
Default limits pass at ≥ 0.05, warn down to 0.01, fail below
Default severity blocking
Applies to every model family
Skipped when the model has no residuals; the data of the model has no time dimension
Version 1.0.0

Parameters (params in a policy)

Field Type Default Description
lags integer 10 ≥ 1, ≤ 200

sensitivity.prior - The results come from the data

Fits a Bayesian model again with wider and narrower starting assumptions. The key answers must change very little, so that you know that they come from the data.

Difficulty: expert. Typical runtime: long.

Use it when: Bayesian models, mainly models with business priors.

Avoid it when: Models that are not Bayesian.

It needs: The data, the settings and the priors of the model.

It assumes: The scales that the check tries include all reasonable prior changes.

You get: The largest relative change of a key estimate.

Example: Prior widths that are two times larger move the return of TV by 6%: pass.

Related: plausibility.signs, recovery.simulated.

Expert detail

Power-scaling or refit-based prior sensitivity: relative change of posterior means of key quantities under scaled prior widths. References: Kallioinen et al. (2023), Detecting and diagnosing prior and likelihood sensitivity with power-scaling.

Category sensitivity
Metric prior_sensitivity
Default limits pass at ≤ 0.2, warn up to 0.5, fail above
Default severity blocking
Applies to every model family
Skipped when only Bayesian models have priors that the check can change
Version 1.0.0

Parameters (params in a policy)

Field Type Default Description
scales list of number [0.5, 2.0] Prior widths tried

stability.rolling_params - Estimates are stable over time

Fits the model again on rolling windows of history. Then it makes sure that the key estimates do not change much from one window to the next.

Difficulty: expert. Typical runtime: long.

Use it when: Models with estimates that you use for decisions (effects, returns).

Avoid it when: Short histories (the windows are then too small).

It needs: The data over time and the settings of the model.

It assumes: The true effects change slowly.

You get: How much each key estimate changes between windows.

Example: The return of TV changes by 12% between windows: pass.

Related: test.cusum.

Expert detail

Coefficient of variation (or max relative deviation from the full-sample estimate) of each watched parameter across rolling-window refits; maximum over parameters.

Category stability
Metric param_variation
Default limits pass at ≤ 0.25, warn up to 0.5, fail above
Default severity blocking
Applies to every model family
Skipped when the data of the model has no time dimension
Version 1.0.0

Parameters (params in a policy)

Field Type Default Description
window integer – Periods per window (≥ 8)
step integer 4 ≥ 1
params list of string – Parameters to watch (default: the key effects)

The default policies

Seeded as tenant-wide policies (version 1) the first time a tenant uses governance. They decide what a validation checks per model family; they do not require approval and do not gate promotion until an admin turns that on.

Policy Families Checks (severity, parameters)
Marketing mix models (platform default) mmm data.quality (blocking)
convergence (blocking)
holdout.accuracy (blocking, metric=mape)
null.permutation_target (blocking, n_permutations=20)
null.noise_regressors (blocking, n_noise=3)
plausibility.signs (blocking)
baseline.beats_naive (warning)
null.placebo_time_shift (warning)
sensitivity.prior (warning)
residuals.whiteness (info)
Forecasting models (platform default) ts, forecast, ts_multivariate, regime data.quality (blocking)
convergence (blocking)
baseline.beats_naive (blocking)
holdout.accuracy (blocking, metric=mape)
residuals.whiteness (warning)
null.permutation_target (warning, n_permutations=20)
stability.rolling_params (info)
Regression and machine-learning models (platform default) tabular, choice, sklearn data.quality (blocking)
holdout.accuracy (blocking, metric=mape)
null.permutation_target (blocking, n_permutations=20)
null.noise_regressors (warning, n_noise=3)
convergence (info)