Skip to main content

khive_fold/objective/
context.rs

1//! Objective evaluation context
2
3use chrono::{DateTime, Utc};
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7/// Context for objective evaluation
8///
9/// `as_of` defaults to the Unix epoch (`DateTime::<Utc>::default()`).
10/// Callers that need "now" must pass it explicitly via [`ObjectiveContext::at`].
11/// This preserves the ADR-024 "no clock" invariant for the foundation layer.
12#[derive(Debug, Clone, Default)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14pub struct ObjectiveContext {
15    /// Evaluation time
16    pub as_of: DateTime<Utc>,
17    /// Maximum candidates to consider
18    pub max_candidates: Option<usize>,
19    /// Minimum score threshold
20    pub min_score: Option<f64>,
21    /// Extra context data
22    #[cfg_attr(feature = "serde", serde(default))]
23    pub extra: serde_json::Value,
24}
25
26impl ObjectiveContext {
27    /// Create a new context with the Unix epoch as `as_of`.
28    ///
29    /// Per ADR-024 ("no clock"), the foundation layer does not call `Utc::now()`.
30    /// Pass an explicit timestamp via [`ObjectiveContext::at`] when a real
31    /// wall-clock instant is needed.
32    pub fn new() -> Self {
33        Self::default()
34    }
35
36    /// Create context for a specific time
37    pub fn at(time: DateTime<Utc>) -> Self {
38        Self {
39            as_of: time,
40            ..Default::default()
41        }
42    }
43
44    /// Set maximum candidates
45    pub fn with_max_candidates(mut self, n: usize) -> Self {
46        self.max_candidates = Some(n);
47        self
48    }
49
50    /// Set minimum score threshold
51    pub fn with_min_score(mut self, score: f64) -> Self {
52        self.min_score = Some(score);
53        self
54    }
55
56    /// Set extra context
57    pub fn with_extra(mut self, extra: serde_json::Value) -> Self {
58        self.extra = extra;
59        self
60    }
61}