# 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