Files
git.stella-ops.org/docs/features/unchecked/policy/exponential-confidence-decay-for-unknown-reachability.md

4.0 KiB

Exponential Confidence Decay for Unknown Reachability (Half-Life Calculator)

Module

Policy

Status

IMPLEMENTED

Description

Exponential half-life decay of confidence scores implemented in DecayedConfidenceCalculator with formula exp(-ln(2) * ageDays / halfLifeDays), configurable half-life (default 14 days), floor value, and metrics emission. Includes ObservationDecay models, uncertainty scoring, signal state tracking, and property-based tests. Integrated into policy determinization gate.

Implementation Details

  • DecayedConfidenceCalculator: src/Policy/__Libraries/StellaOps.Policy.Determinization/Scoring/DecayedConfidenceCalculator.cs (sealed class implements IDecayedConfidenceCalculator)
    • Calculate(baseConfidence, ageDays, halfLifeDays=14.0, floor=0.1) -- applies exponential decay: max(floor, baseConfidence * exp(-ln(2) * ageDays / halfLifeDays))
    • CalculateDecayFactor(ageDays, halfLifeDays=14.0) -- returns raw decay factor clamped to [0.0, 1.0]
    • Parameter validation: baseConfidence [0.0-1.0], ageDays >= 0, halfLifeDays > 0, floor [0.0-1.0]
    • Metrics emission: stellaops_determinization_decay_multiplier histogram with half_life_days and age_days tags
  • ObservationDecay: src/Policy/__Libraries/StellaOps.Policy.Determinization/Models/ObservationDecay.cs (sealed record)
    • ObservedAt, RefreshedAt, HalfLifeDays (default 14.0), Floor (default 0.35), StalenessThreshold (default 0.50)
    • CalculateDecay(now) computes current multiplier: max(Floor, exp(-ln(2) * ageDays / HalfLifeDays))
    • CheckIsStale(now) returns true if decay multiplier < StalenessThreshold
    • Factory methods: Create(observedAt, refreshedAt?), Fresh(now), WithSettings(observedAt, refreshedAt, halfLifeDays, floor, stalenessThreshold)
    • Pre-computed fields: AgeDays, DecayedMultiplier, IsStale, LastSignalUpdate
  • DeterminizationOptions: src/Policy/__Libraries/StellaOps.Policy.Determinization/DeterminizationOptions.cs -- global decay configuration
  • DeterminizationContext: src/Policy/__Libraries/StellaOps.Policy.Determinization/Models/DeterminizationContext.cs -- evaluation context with observation state
  • DeterminizationResult: src/Policy/__Libraries/StellaOps.Policy.Determinization/Models/DeterminizationResult.cs -- result including decayed confidence
  • DeterminizationGate: src/Policy/StellaOps.Policy.Engine/Gates/Determinization/DeterminizationGate.cs -- policy gate using DecayedConfidenceCalculator
  • Property-based tests: src/Policy/__Tests/StellaOps.Policy.Determinization.Tests/PropertyTests/DecayPropertyTests.cs -- FsCheck property tests for decay formula
  • Unit tests: src/Policy/__Tests/StellaOps.Policy.Determinization.Tests/DecayedConfidenceCalculatorTests.cs
  • ObservationDecay tests: src/Policy/__Tests/StellaOps.Policy.Determinization.Tests/Models/ObservationDecayTests.cs

E2E Test Plan

  • Calculate decay with baseConfidence=1.0, ageDays=0: verify result=1.0 (no decay)
  • Calculate decay with baseConfidence=1.0, ageDays=14.0, halfLifeDays=14.0: verify result~0.5 (one half-life)
  • Calculate decay with baseConfidence=1.0, ageDays=28.0, halfLifeDays=14.0: verify result~0.25 (two half-lives)
  • Calculate decay with baseConfidence=0.8, ageDays=100.0, halfLifeDays=14.0: verify result=floor (0.1)
  • Calculate decay with custom floor=0.3: verify result never drops below 0.3
  • CalculateDecayFactor with ageDays=7, halfLifeDays=14: verify factor~0.707
  • ObservationDecay.Create with observedAt 30 days ago: verify CheckIsStale(now)=true (decay < 0.50 staleness threshold)
  • ObservationDecay.Fresh(now): verify CalculateDecay(now)=1.0, CheckIsStale(now)=false
  • ObservationDecay.WithSettings(halfLifeDays=7, floor=0.2, stalenessThreshold=0.60): verify custom settings applied
  • Verify DeterminizationGate uses decayed confidence in gate evaluation; stale observation triggers gate warning or block
  • Verify stellaops_determinization_decay_multiplier histogram metric is recorded after Calculate() call