Skip to content

Confidence decay for signal groups (ADR 022)#122

Merged
lance0 merged 1 commit into
mainfrom
feature/confidence-decay
May 11, 2026
Merged

Confidence decay for signal groups (ADR 022)#122
lance0 merged 1 commit into
mainfrom
feature/confidence-decay

Conversation

@lance0
Copy link
Copy Markdown
Owner

@lance0 lance0 commented May 11, 2026

Summary

Adds exponential confidence decay to signal-group derived_confidence computation. Older per-event confidence contributions are multiplied by 0.5 ^ (age_seconds / half_life_seconds), so corroborating evidence from earlier in the correlation window smoothly loses weight without ever being discarded.

Closes ROADMAP item: "Confidence decay over time" under Confidence Model.

Why

The correlation engine (ADR 018) computes derived_confidence as a source-weighted average. With long correlation windows (e.g. window_seconds: 3600) and ADR 021 corroborating signals, evidence that fired hours ago continues to inflate the score at full weight long after the underlying signal has gone silent. A naive cutoff loses history and produces step discontinuities; exponential decay shapes contributions smoothly.

Design (ADR 022)

  • Global config: correlation.confidence_decay_half_life_seconds: u32 (default 0, disabled). Validated 0 ≤ H ≤ 10 × window_seconds.
  • Per-playbook override: correlation_override.confidence_decay_half_life_seconds: Option<u32>. Some(0) explicitly disables for the playbook even when the global is non-zero; None falls through.
  • Compute paths: event ingestion and a new reconcile-loop refresh_decayed_confidence step (no-op when decay disabled).
  • One-shot corroboration_met: sticky — once true, decay never reverts it to false. Preserves the invariant that authorized mitigations stay authorized.
  • Metric: prefixd_signal_group_decay_refreshes_total counter ticks per reconcile step.
  • UI: Group detail page surfaces "decayed, half-life Ns" when active.

Tests

  • 7 engine unit tests (decay math, single-event no-op, multi-event weighting, disabled fallback).
  • 7 config unit tests (override resolution, validation bounds, default).
  • 3 integration tests:
    • test_decay_refresh_lowers_derived_confidence
    • test_decay_one_shot_corroboration_no_flap_back
    • test_decay_disabled_does_not_touch_groups

Full suite: 236 unit + 130 integration + 16 postgres pass. Frontend: 87 tests + build clean. cargo fmt --check + cargo clippy --all-targets --features test-utils -- -D warnings pass.

Migration

None. Default confidence_decay_half_life_seconds: 0 preserves ADR 018 behavior bit-for-bit. Operators opt in by setting a non-zero value in correlation.yaml.

ADR

See docs/adr/022-confidence-decay.md for full rationale, alternatives considered, and consequences.

Exponential time decay applied to per-event confidence contributions when
computing derived_confidence. An event's effective weight is multiplied
by 0.5^(age_seconds / half_life_seconds), so older corroborating evidence
smoothly loses influence without ever being discarded.

- Global config: correlation.confidence_decay_half_life_seconds: u32
  (default 0, disabled). Validated 0 <= H <= 10 * window_seconds.
- Per-playbook override: confidence_decay_half_life_seconds: Option<u32>.
  Some(0) explicitly disables for the playbook; None falls through.
- Reconcile loop step refresh_decayed_confidence iterates open groups
  every tick and recomputes derived_confidence with decay applied.
- Sticky corroboration_met: once true, decay never flips it back to
  false (one-shot authorization).
- Metric prefixd_signal_group_decay_refreshes_total counter.
- UI label "decayed, half-life Ns" on group detail page when active.

Default disabled, zero behavior change for existing deployments.

Tests: 17 new (7 engine + 7 config + 3 integration) covering decay
math, override resolution, validation, disabled paths, and sticky
corroboration semantics.
@lance0 lance0 merged commit 3d96de3 into main May 11, 2026
5 checks passed
@lance0 lance0 deleted the feature/confidence-decay branch May 11, 2026 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant