Skip to main content

converge_pack/
context.rs

1// Copyright 2024-2026 Reflective Labs
2// SPDX-License-Identifier: MIT
3
4//! Context keys and the shared context contract.
5//!
6//! Context is the API. Suggestors don't call each other — they read from and
7//! write to shared context through typed keys.
8
9use serde::{Deserialize, Serialize};
10
11use crate::fact::{Fact, ProposedFact};
12
13/// Typed keys for the shared context namespace.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
15#[cfg_attr(feature = "strum", derive(strum::EnumIter))]
16pub enum ContextKey {
17    /// Initial inputs from the root intent. Set once at initialization.
18    Seeds,
19    /// Proposed ideas and hypotheses from analysis suggestors.
20    Hypotheses,
21    /// Action plans and strategic recommendations.
22    Strategies,
23    /// Limitations, rules, and boundary conditions.
24    Constraints,
25    /// Observations, market data, and signals from the environment.
26    Signals,
27    /// Competitive intelligence and comparisons.
28    Competitors,
29    /// Assessments, ratings, and evaluations of other facts.
30    Evaluations,
31    /// LLM-generated suggestions awaiting validation.
32    Proposals,
33    /// Error and debugging information. Never blocks convergence.
34    Diagnostic,
35}
36
37/// Read-only view of the shared context.
38///
39/// Suggestors receive `&dyn Context` during `accepts()` and `execute()`.
40/// They cannot mutate it directly — mutations happen through `AgentEffect`
41/// after the engine collects all effects and merges them deterministically.
42pub trait Context: Send + Sync {
43    /// Check whether any facts exist under this key.
44    fn has(&self, key: ContextKey) -> bool;
45
46    /// Get all facts under this key.
47    fn get(&self, key: ContextKey) -> &[Fact];
48
49    /// Get all proposed facts (unvalidated).
50    fn get_proposals(&self, key: ContextKey) -> &[ProposedFact] {
51        let _ = key;
52        &[]
53    }
54
55    /// Count of facts under a key.
56    fn count(&self, key: ContextKey) -> usize {
57        self.get(key).len()
58    }
59}