Skip to main content

converge_pack/
effect.rs

1// Copyright 2024-2026 Reflective Labs
2// SPDX-License-Identifier: MIT
3
4//! Suggestor effects — what suggestors produce, the engine merges.
5//!
6//! Effects are proposal-only. Suggestors suggest; the engine validates and promotes.
7
8use crate::context::ContextKey;
9use crate::fact::ProposedFact;
10
11/// The output of a suggestor's `execute()` call.
12///
13/// An effect describes what a suggestor wants to suggest to the context.
14/// The engine collects effects from all eligible suggestors, validates them,
15/// and promotes them serially in deterministic order.
16#[derive(Debug, Default)]
17pub struct AgentEffect {
18    /// New proposals to be validated by the engine.
19    pub proposals: Vec<ProposedFact>,
20}
21
22impl AgentEffect {
23    /// Creates an empty effect (no contributions).
24    #[must_use]
25    pub fn empty() -> Self {
26        Self::default()
27    }
28
29    /// Creates an effect with a single proposal.
30    #[must_use]
31    pub fn with_proposal(proposal: ProposedFact) -> Self {
32        Self {
33            proposals: vec![proposal],
34        }
35    }
36
37    /// Creates an effect with multiple proposals.
38    #[must_use]
39    pub fn with_proposals(proposals: Vec<ProposedFact>) -> Self {
40        Self { proposals }
41    }
42
43    /// Returns true if this effect contributes nothing.
44    #[must_use]
45    pub fn is_empty(&self) -> bool {
46        self.proposals.is_empty()
47    }
48
49    /// Returns the context keys affected by this effect.
50    #[must_use]
51    pub fn affected_keys(&self) -> Vec<ContextKey> {
52        let mut keys: Vec<ContextKey> = self.proposals.iter().map(|p| p.key).collect();
53        keys.sort();
54        keys.dedup();
55        keys
56    }
57}