5.4 KiB
StellaOps.Policy.Determinization - Agent Guide
Module Overview
The Determinization library handles CVEs that arrive without complete evidence (EPSS, VEX, reachability). It treats unknown observations as probabilistic with entropy-weighted trust that matures as evidence arrives.
Key Concepts:
ObservationState: Lifecycle state for CVE observations (PendingDeterminization, Determined, Disputed, etc.)SignalState<T>: Null-aware wrapper distinguishing "not queried" from "queried but absent"UncertaintyScore: Knowledge completeness measurement (high entropy = missing signals)ObservationDecay: Time-based confidence decay with configurable half-lifeGuardRails: Monitoring requirements when allowing uncertain observations
Directory Structure
src/Policy/__Libraries/StellaOps.Policy.Determinization/
├── Models/ # Core data models
│ ├── ObservationState.cs
│ ├── SignalState.cs
│ ├── SignalSnapshot.cs
│ ├── UncertaintyScore.cs
│ ├── ObservationDecay.cs
│ ├── GuardRails.cs
│ └── DeterminizationContext.cs
├── Evidence/ # Signal evidence types
│ ├── EpssEvidence.cs
│ ├── VexClaimSummary.cs
│ ├── ReachabilityEvidence.cs
│ └── ...
├── Scoring/ # Calculation services
│ ├── UncertaintyScoreCalculator.cs
│ ├── DecayedConfidenceCalculator.cs
│ ├── TrustScoreAggregator.cs
│ └── SignalWeights.cs
├── Policies/ # Policy rules (in Policy.Engine)
└── DeterminizationOptions.cs
Key Patterns
1. SignalState Usage
Always use SignalState<T> to wrap signal values:
// Good - explicit status
var epss = SignalState<EpssEvidence>.WithValue(evidence, queriedAt, "first.org");
var vex = SignalState<VexClaimSummary>.Absent(queriedAt, "vendor");
var reach = SignalState<ReachabilityEvidence>.NotQueried();
var failed = SignalState<CvssEvidence>.Failed("Timeout");
// Bad - nullable without status
EpssEvidence? epss = null; // Can't tell if not queried or absent
2. Uncertainty Calculation
Entropy = 1 - (weighted present signals / max weight):
// All signals present = 0.0 entropy (fully certain)
// No signals present = 1.0 entropy (fully uncertain)
// Formula uses configurable weights per signal type
3. Decay Calculation
Exponential decay with floor:
decayed = max(floor, exp(-ln(2) * age_days / half_life_days))
// Default: 14-day half-life, 0.35 floor
// After 14 days: ~50% confidence
// After 28 days: ~35% confidence (floor)
4. Policy Rules
Rules evaluate in priority order (lower = first):
| Priority | Rule | Outcome |
|---|---|---|
| 10 | Runtime shows loaded | Escalated |
| 20 | EPSS >= threshold | Blocked |
| 25 | Proven reachable | Blocked |
| 30 | High entropy in prod | Blocked |
| 40 | Evidence stale | Deferred |
| 50 | Uncertain + non-prod | GuardedPass |
| 60 | Unreachable + confident | Pass |
| 70 | Sufficient evidence | Pass |
| 100 | Default | Deferred |
Testing Guidelines
Unit Tests Required
SignalState<T>factory methodsUncertaintyScoreCalculatorentropy bounds [0.0, 1.0]DecayedConfidenceCalculatorhalf-life formula- Policy rule priority ordering
- State transition logic
Property Tests
- Entropy always in [0.0, 1.0]
- Decay monotonically decreasing with age
- Same snapshot produces same uncertainty
Integration Tests
- DI registration with configuration
- Signal snapshot building
- Policy gate evaluation
Configuration
Determinization:
EpssQuarantineThreshold: 0.4
GuardedAllowScoreThreshold: 0.5
GuardedAllowEntropyThreshold: 0.4
ProductionBlockEntropyThreshold: 0.3
DecayHalfLifeDays: 14
DecayFloor: 0.35
GuardedReviewIntervalDays: 7
MaxGuardedDurationDays: 30
SignalWeights:
Vex: 0.25
Epss: 0.15
Reachability: 0.25
Runtime: 0.15
Backport: 0.10
SbomLineage: 0.10
Common Pitfalls
-
Don't confuse EntropySignal with UncertaintyScore:
EntropySignalmeasures code complexity;UncertaintyScoremeasures knowledge completeness. -
Always inject TimeProvider: Never use
DateTime.UtcNowdirectly for decay calculations. -
Normalize weights before calculation: Call
SignalWeights.Normalize()to ensure weights sum to 1.0. -
Check signal status before accessing value:
signal.HasValuemust be true before usingsignal.Value!. -
Handle all ObservationStates: Switch expressions must be exhaustive.
Dependencies
StellaOps.Policy(PolicyVerdictStatus, existing confidence models)System.Collections.Immutable(ImmutableArray for collections)Microsoft.Extensions.Options(configuration)Microsoft.Extensions.Logging(logging)
Related Modules
- Policy.Engine: DeterminizationGate integrates with policy pipeline
- Feedser: Signal attachers emit SignalState
- VexLens: VEX updates emit SignalUpdatedEvent
- Graph: CVE nodes carry ObservationState and UncertaintyScore
- Findings: Observation persistence and audit trail
Sprint References
- SPRINT_20260106_001_001_LB: Core models
- SPRINT_20260106_001_002_LB: Scoring services
- SPRINT_20260106_001_003_POLICY: Policy integration
- SPRINT_20260106_001_004_BE: Backend integration
- SPRINT_20260106_001_005_FE: Frontend UI