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}