Skip to content

ADR-006: Posterior Predictive for Bayesian Gates

Status: Implemented Date: 2026-01-31 Decision Makers: Risk Team, Engineering Related Gap: NEW-A (Multi-Model Coherence Review) Implementation: src/engine/bayesian.py


Context

The AEGIS Bayesian gates compute the probability that a threshold is exceeded, P(Δ ≥ t | y), where y is the observed data. However, there is a subtle but important distinction between:

  1. Posterior distribution: P(θ ≥ t | y) - probability that the latent parameter θ exceeds threshold
  2. Posterior predictive: P(Y_new ≥ t | y) - probability that a future observation Y_new exceeds threshold

For governance decisions, we care about whether future realized values will exceed thresholds, not what the underlying parameter is. This means we should be using the posterior predictive distribution.

The difference is that the posterior predictive accounts for observation noise (σ_L) in addition to posterior uncertainty (σ_post):

  • Posterior std: σ_post
  • Posterior predictive std: σ_pred = √(σ_post² + σ_L²)

Using only the posterior underestimates uncertainty about future observations, potentially leading to overconfident gate decisions.

Decision Drivers

Driver Weight Notes
Mathematical correctness HIGH Posterior predictive is the correct distribution for future predictions
Conservative governance HIGH Underestimating uncertainty leads to false confidence
Backward compatibility MEDIUM Existing behavior should remain available
Implementation simplicity LOW Formula change is straightforward

Options Considered

Option 1: Always Use Posterior Predictive

Approach: Replace posterior std with posterior predictive std in all gate calculations.

Pros: - Mathematically correct for decision-making about future outcomes - More conservative (wider uncertainty bounds) - Simpler mental model

Cons: - Breaking change for existing calibrated thresholds - Gates will be slightly more conservative

Verdict: REJECTED - Too disruptive to existing deployments

Option 2: Opt-In Posterior Predictive (Selected)

Approach: Add use_predictive: bool = False parameter to compute_posterior() and a new compute_posterior_predictive() method. Existing behavior unchanged by default.

Pros: - Backward compatible - Allows gradual migration - Users can choose appropriate method

Cons: - Two code paths to maintain - Users must know to opt-in

Verdict: SELECTED - Best balance of correctness and compatibility

Option 3: Recalibrate All Thresholds

Approach: Adopt posterior predictive as default, recalibrate all thresholds to maintain equivalent gate behavior.

Pros: - Clean transition - Only one recommended approach

Cons: - Requires extensive testing - Deployment coordination required - Threshold recalibration is complex

Verdict: REJECTED - Too much coordination overhead

Decision

Implement Option 2: Add posterior predictive as an opt-in feature.

Mathematical Formulation

Current (posterior):

z = (threshold - μ_post) / σ_post
P(θ ≥ t | y) = 1 - Φ(z)

New (posterior predictive):

σ_pred = √(σ_post² + σ_L²)
z_pred = (threshold - μ_post) / σ_pred
P(Y_new ≥ t | y) = 1 - Φ(z_pred)

Where: - μ_post: posterior mean - σ_post: posterior standard deviation - σ_L: likelihood (observation) standard deviation - Φ: standard normal CDF

API Changes

  1. Add compute_posterior_predictive() method:

    def compute_posterior_predictive(
        self,
        observed_delta: float,
        trigger_threshold: float,
        ...
    ) -> float:
        """Compute P(Y_new ≥ threshold | observed_delta) using posterior predictive."""
    

  2. Add use_predictive parameter to existing methods:

    def compute_posterior(
        self,
        observed_delta: float,
        trigger_threshold: float,
        use_predictive: bool = False,  # NEW
        ...
    ) -> float:
    

Consequences

Positive

  • Mathematically correct predictions for future observations
  • More conservative gate decisions (safer for governance)
  • Explicit choice between posterior and posterior predictive
  • No breaking changes to existing deployments

Negative

  • Two code paths to maintain (low maintenance burden)
  • Users must understand distinction to choose correctly
  • Slightly more complex API

Neutral

  • Performance impact negligible (one sqrt call)
  • Documentation needed to explain when to use each

Implementation Plan

  1. Add compute_posterior_predictive() method to BayesianPosterior
  2. Add use_predictive parameter to compute_posterior() and compute_full()
  3. Add tests verifying:
  4. Predictive std > posterior std (always)
  5. Predictive probability more conservative
  6. Backward compatibility preserved
  7. Document in docstrings when to use each variant
  8. Consider making predictive default in v2.0

References

  • Multi-Model Coherence Review (Issue NEW-A)
  • Gelman et al., "Bayesian Data Analysis", Ch. 1 on posterior predictive
  • Bishop, "Pattern Recognition and Machine Learning", §1.2.5

Changelog

Date Author Changes
2026-01-31 Engineering Initial proposal