Skip to main content

recursive_kernel_core/
lib.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use stack_ids::{ConstraintId, KernelRunId, OperatorId, OracleSliceId, ScopeKey, SyndromeId};
4
5pub const CONSTRAINT_COMPILER_OPERATOR_ID: &str = "rik.constraint_compiler.v1";
6pub const RECURSIVE_MESSAGE_PASSING_OPERATOR_ID: &str = "rik.message_passing.v1";
7
8pub const CONSTRAINT_COMPILER_OPERATOR_NAME: &str = "constraint_compiler";
9pub const RECURSIVE_MESSAGE_PASSING_OPERATOR_NAME: &str = "recursive_message_passing";
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
12#[serde(rename_all = "snake_case")]
13pub enum ExactnessClass {
14    Exact,
15    Conservative,
16    Approximate,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
20#[serde(rename_all = "snake_case")]
21pub enum ConvergenceKind {
22    AcyclicSinglePass,
23    ResidualBounded,
24    FixedPoint,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
28#[serde(rename_all = "snake_case")]
29pub enum StopRule {
30    MaxIterations { limit: u32 },
31    ResidualBelow { micros: u64 },
32    AcyclicCompletion,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
36#[serde(rename_all = "snake_case")]
37pub enum DegradationBehavior {
38    Blocked,
39    AdvisoryOnly,
40    ConservativeFallback,
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
44#[serde(rename_all = "snake_case")]
45pub enum ArtifactAuthorityClass {
46    NonAuthoritativeDerived,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
50pub struct OperatorContract {
51    pub inputs: Vec<String>,
52    pub outputs: Vec<String>,
53    pub exactness: ExactnessClass,
54    pub convergence: ConvergenceKind,
55    pub stop_rule: StopRule,
56    pub degradation: DegradationBehavior,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
60pub struct OperatorMetadata {
61    pub operator_id: OperatorId,
62    pub name: String,
63    pub contract: OperatorContract,
64}
65
66impl OperatorMetadata {
67    /// Validates the minimal operator metadata contract exposed to downstream schedulers.
68    pub fn validate(&self) -> Result<(), String> {
69        if self.name.trim().is_empty() {
70            return Err("operator name must not be empty".into());
71        }
72        if self.contract.inputs.is_empty() {
73            return Err("operator inputs must not be empty".into());
74        }
75        if self.contract.outputs.is_empty() {
76            return Err("operator outputs must not be empty".into());
77        }
78        Ok(())
79    }
80}
81
82/// Returns the canonical metadata for the bounded constraint-compiler operator.
83pub fn constraint_compiler_operator() -> OperatorMetadata {
84    OperatorMetadata {
85        operator_id: OperatorId::new(CONSTRAINT_COMPILER_OPERATOR_ID),
86        name: CONSTRAINT_COMPILER_OPERATOR_NAME.to_string(),
87        contract: OperatorContract {
88            inputs: vec!["projection_import_batch_v3".into()],
89            outputs: vec!["inference_graph".into()],
90            exactness: ExactnessClass::Exact,
91            convergence: ConvergenceKind::AcyclicSinglePass,
92            stop_rule: StopRule::AcyclicCompletion,
93            degradation: DegradationBehavior::AdvisoryOnly,
94        },
95    }
96}
97
98/// Returns the canonical metadata for the bounded recursive message-passing operator.
99pub fn message_passing_operator() -> OperatorMetadata {
100    OperatorMetadata {
101        operator_id: OperatorId::new(RECURSIVE_MESSAGE_PASSING_OPERATOR_ID),
102        name: RECURSIVE_MESSAGE_PASSING_OPERATOR_NAME.to_string(),
103        contract: OperatorContract {
104            inputs: vec!["inference_graph".into()],
105            outputs: vec!["execution_report".into()],
106            exactness: ExactnessClass::Conservative,
107            convergence: ConvergenceKind::FixedPoint,
108            stop_rule: StopRule::ResidualBelow { micros: 1_000 },
109            degradation: DegradationBehavior::AdvisoryOnly,
110        },
111    }
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
115pub struct KernelRun {
116    pub run_id: KernelRunId,
117    pub policy_version: String,
118    pub scope_key: ScopeKey,
119    pub operator_ids: Vec<OperatorId>,
120    pub oracle_slice_id: Option<OracleSliceId>,
121    pub degradation_active: bool,
122}
123
124impl KernelRun {
125    /// Marks every kernel run artifact as derived rather than directly authoritative.
126    pub fn authority_class(&self) -> ArtifactAuthorityClass {
127        ArtifactAuthorityClass::NonAuthoritativeDerived
128    }
129}
130
131#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
132pub struct ConstraintUnit {
133    pub constraint_id: ConstraintId,
134    pub kind: String,
135    pub variable_ids: Vec<String>,
136    pub operator_id: OperatorId,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
140pub struct Syndrome {
141    pub syndrome_id: SyndromeId,
142    pub signature: String,
143    pub blocked_by_degradation: bool,
144}
145
146#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
147#[serde(rename_all = "snake_case")]
148pub enum KernelArtifactKind {
149    Residual,
150    Syndrome,
151    Witness,
152    Certificate,
153    CalibrationReport,
154    KernelRefutationResult,
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
158pub struct ResidualArtifact {
159    pub constraint_id: ConstraintId,
160    pub residual_micros: u64,
161    pub monotone_nonincreasing: bool,
162}
163
164#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
165pub struct WitnessArtifact {
166    pub witness_id: String,
167    pub target_node_id: String,
168    pub supporting_constraint_ids: Vec<ConstraintId>,
169    pub belief_micros: u64,
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
173pub struct CertificateArtifact {
174    pub certificate_id: String,
175    pub certified_node_id: String,
176    pub satisfied_constraint_ids: Vec<ConstraintId>,
177    pub oracle_slice_id: Option<OracleSliceId>,
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
181pub struct CalibrationReport {
182    pub nuisance_node_ids: Vec<String>,
183    pub caveats: Vec<String>,
184    pub blocked: bool,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
188#[serde(rename_all = "snake_case")]
189pub enum KernelRefutationOutcome {
190    FlipWitnessFound,
191    NoFlipFoundWithinBudget,
192    Unsupported,
193}
194
195#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
196pub struct PerturbationWitness {
197    pub changed_node_ids: Vec<String>,
198    pub changed_constraint_ids: Vec<ConstraintId>,
199    pub budget_used: u32,
200}
201
202#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
203pub struct KernelRefutationResult {
204    pub target_node_id: String,
205    pub outcome: KernelRefutationOutcome,
206    pub witness: Option<PerturbationWitness>,
207    pub notes: Vec<String>,
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213
214    #[test]
215    fn operator_contract_requires_inputs_outputs_and_stop_rule() {
216        let operator = constraint_compiler_operator();
217        assert_eq!(
218            operator.operator_id.as_str(),
219            CONSTRAINT_COMPILER_OPERATOR_ID
220        );
221        assert_eq!(operator.name, CONSTRAINT_COMPILER_OPERATOR_NAME);
222        assert_eq!(operator.contract.inputs, vec!["projection_import_batch_v3"]);
223        assert_eq!(operator.contract.outputs, vec!["inference_graph"]);
224        assert!(matches!(operator.contract.exactness, ExactnessClass::Exact));
225        operator.validate().unwrap();
226    }
227
228    #[test]
229    fn kernel_artifacts_remain_non_authoritative() {
230        let run = KernelRun {
231            run_id: KernelRunId::new("run-1"),
232            policy_version: "policy-1".into(),
233            scope_key: ScopeKey::namespace_only("ns"),
234            operator_ids: vec![OperatorId::new(CONSTRAINT_COMPILER_OPERATOR_ID)],
235            oracle_slice_id: None,
236            degradation_active: false,
237        };
238
239        assert_eq!(
240            run.authority_class(),
241            ArtifactAuthorityClass::NonAuthoritativeDerived
242        );
243    }
244
245    #[test]
246    fn shared_artifact_schemas_are_serializable() {
247        let witness = WitnessArtifact {
248            witness_id: "wit-1".into(),
249            target_node_id: "node-1".into(),
250            supporting_constraint_ids: vec![ConstraintId::new("constraint:1")],
251            belief_micros: 750_000,
252        };
253        let encoded = serde_json::to_string(&witness).unwrap();
254        assert!(encoded.contains("wit-1"));
255    }
256}