Skip to main content

sim_lib_forge/
resolve.rs

1use sim_kernel::{Cx, EvalFabric, Result};
2
3use crate::{
4    CompiledIntent, IntentLibrary, IntentStatus, LiftOptions, VerifyCatalog, forge_lift_once,
5    normalize_prose,
6};
7
8/// Promotion rule applied after a resolve miss lifts a fresh candidate.
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum PromotePolicy {
11    /// Keep the structurally checked artifact as a candidate.
12    KeepCandidate,
13    /// Promote only after semantic probes pass.
14    ///
15    /// The resolver uses its verifier catalog for probe lookup, and missing or
16    /// failing probes leave the artifact as a candidate.
17    AutoVerifiedOnProbePass,
18    /// Require an approval record before any golden artifact is created.
19    RequireHumanApprovalForGolden,
20}
21
22/// Stateful FORGE resolver with an intent library index.
23#[derive(Clone, Debug)]
24pub struct ForgeResolver {
25    library: IntentLibrary,
26    lift_options: LiftOptions,
27    verify_catalog: VerifyCatalog,
28}
29
30impl ForgeResolver {
31    /// Builds a resolver from an existing intent library and lift options.
32    pub fn new(library: IntentLibrary, lift_options: LiftOptions) -> Self {
33        Self::new_with_verifiers(library, lift_options, VerifyCatalog::new())
34    }
35
36    /// Builds a resolver from an existing intent library, lift options, and
37    /// semantic verifier catalog.
38    pub fn new_with_verifiers(
39        library: IntentLibrary,
40        lift_options: LiftOptions,
41        verify_catalog: VerifyCatalog,
42    ) -> Self {
43        Self {
44            library,
45            lift_options,
46            verify_catalog,
47        }
48    }
49
50    /// Returns the intent library backing this resolver.
51    pub fn library(&self) -> &IntentLibrary {
52        &self.library
53    }
54
55    /// Returns a mutable handle to the intent library backing this resolver.
56    pub fn library_mut(&mut self) -> &mut IntentLibrary {
57        &mut self.library
58    }
59
60    /// Returns the semantic verifier catalog backing this resolver.
61    pub fn verify_catalog(&self) -> &VerifyCatalog {
62        &self.verify_catalog
63    }
64
65    /// Returns a mutable semantic verifier catalog handle.
66    pub fn verify_catalog_mut(&mut self) -> &mut VerifyCatalog {
67        &mut self.verify_catalog
68    }
69
70    /// Resolves prose to a compiled intent, reusing a golden source hit.
71    ///
72    /// A golden hit returns directly and does not call the lift fabric. A miss
73    /// lifts a structurally checked candidate, stores it in the named index,
74    /// and leaves it in `Candidate` state until semantic verification or human
75    /// approval exists.
76    pub fn resolve(
77        &mut self,
78        cx: &mut Cx,
79        target: &dyn EvalFabric,
80        prose: &str,
81        policy: PromotePolicy,
82    ) -> Result<CompiledIntent> {
83        let (_, source) = normalize_prose(prose)?;
84        if let Some(intent) = self.library.golden_by_source(&source) {
85            return Ok(intent.clone());
86        }
87
88        let mut lifted = forge_lift_once(cx, target, prose, &self.lift_options)?;
89        self.apply_policy(cx, &mut lifted, policy)?;
90        self.library.store_resolved(lifted)
91    }
92
93    fn apply_policy(
94        &self,
95        cx: &mut Cx,
96        intent: &mut CompiledIntent,
97        policy: PromotePolicy,
98    ) -> Result<()> {
99        intent.status = IntentStatus::Candidate;
100        match policy {
101            PromotePolicy::KeepCandidate | PromotePolicy::RequireHumanApprovalForGolden => {}
102            PromotePolicy::AutoVerifiedOnProbePass => {
103                intent.probes = self.verify_catalog.probe_ids_for(&intent.name);
104                let report = self.verify_catalog.verify_intent_probes(cx, intent)?;
105                if report.accepted() {
106                    intent.status = IntentStatus::Verified;
107                }
108            }
109        }
110        Ok(())
111    }
112}
113
114impl Default for ForgeResolver {
115    fn default() -> Self {
116        Self::new(IntentLibrary::new(), LiftOptions::default())
117    }
118}
119
120/// Resolves prose through an empty in-memory intent library.
121///
122/// Callers that need compile-once reuse across calls should keep a
123/// [`ForgeResolver`] or call [`forge_resolve_with_options`] with their own
124/// [`IntentLibrary`].
125pub fn forge_resolve(
126    cx: &mut Cx,
127    target: &dyn EvalFabric,
128    prose: &str,
129    policy: PromotePolicy,
130) -> Result<CompiledIntent> {
131    ForgeResolver::default().resolve(cx, target, prose, policy)
132}
133
134/// Resolves prose through an explicit library and lift options.
135pub fn forge_resolve_with_options(
136    library: &mut IntentLibrary,
137    lift_options: &LiftOptions,
138    cx: &mut Cx,
139    target: &dyn EvalFabric,
140    prose: &str,
141    policy: PromotePolicy,
142) -> Result<CompiledIntent> {
143    let mut resolver = ForgeResolver::new(std::mem::take(library), lift_options.clone());
144    let intent = resolver.resolve(cx, target, prose, policy)?;
145    *library = resolver.library;
146    Ok(intent)
147}