Skip to main content

aristo_core/index/
enums.rs

1//! Enum types for `.aristo/index.toml`.
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6/// Annotation kind: `intent` (verifiable claim about code) or `assume`
7/// (external invariant the code relies on, taken as given).
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
9#[serde(rename_all = "lowercase")]
10pub enum AnnotationKind {
11    Intent,
12    Assume,
13}
14
15/// Current verification state. The wire form uses kebab-case to keep
16/// `pending-deepen` readable on disk.
17///
18/// `Ord` follows declaration order (most-trusted first), which gives the
19/// `aristo metrics --json` `status_distribution` map a stable key order.
20#[derive(
21    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
22)]
23#[serde(rename_all = "kebab-case")]
24pub enum Status {
25    /// Verified at the strongest method available; signature is current.
26    Verified,
27    /// Verified via assertion-style testing (mined assertions, free or paid).
28    Tested,
29    /// Verified via neural reasoning (LLM-based).
30    Neural,
31    /// Was verified at a prior code state; current `body_hash` differs from
32    /// the signed outcome. Sig still valid; only the code drifted.
33    Stale,
34    /// `verified_outcome.commit_hash` is not in this repository's history.
35    /// Most likely cause: index entry copied from another repo (B5b).
36    Orphan,
37    /// `verified_outcome` does not verify against any bundled public key —
38    /// either tampered or signed by a revoked/unknown key (B5b).
39    Forged,
40    /// Has not yet been verified, or was reverted to unknown after a rename
41    /// or rebind operation invalidated the prior signature.
42    Unknown,
43    /// Sig is valid but commit ancestry could not be confirmed because the
44    /// outcome's commit falls outside a shallow checkout's window (B5b).
45    PendingDeepen,
46    /// Verification produced a counterexample — the property does NOT
47    /// hold under the cited code/text version. Terminal state: stays
48    /// Counterexample until either `body_hash` drifts (→ Stale) or the
49    /// intent text is rewritten to exclude the refuted case. There is
50    /// NO accept-counterexample flow; counterexamples are loud and
51    /// surface on every `aristo stamp` until the user fixes them.
52    Counterexample,
53    /// Verification was inconclusive — the verifier could not discharge
54    /// the proof's gap and queued one or more suggested annotations the
55    /// user could add to close it. Distinct from Unknown (which means
56    /// "never attempted") so the skip logic can tell "we tried and got
57    /// stuck" from "we haven't tried yet." Stays Inconclusive until
58    /// either the source drifts (→ Stale via the body-drift rule), the
59    /// user adopts a queued suggestion (→ re-verify), or the user
60    /// explicitly re-runs with `--rerun`.
61    Inconclusive,
62}
63
64#[aristo::intent(
65    "The terminal-clean states are exactly Verified, Tested, and Neural — \
66     an intent whose current code IS confirmed to hold under some \
67     verification method. Stale, Counterexample, Inconclusive, Unknown, \
68     and the B5b error states (Orphan / Forged / PendingDeepen) are NOT \
69     clean: each means the property is unproven, refuted, or untrusted for \
70     the current code. This is the single definition every surface — \
71     status counts, the badge rate, and the nudge engine's unverified \
72     backlog — shares, so they can never disagree on what 'verified' means.",
73    verify = "test",
74    id = "status_terminal_clean_is_verified_tested_neural"
75)]
76impl Status {
77    /// True iff this status is a terminal-clean (confirmed-verified) state.
78    pub fn is_terminal_clean(self) -> bool {
79        matches!(self, Status::Verified | Status::Tested | Status::Neural)
80    }
81}
82
83/// `verify` field value. Mixed-type per design (G1):
84///
85/// - `false` (TOML bool): documentation only; no check ever runs.
86/// - `true` (TOML bool): resolves to project default in `aristo.toml [verify] default_method`.
87/// - one of `"neural"`, `"test"`, `"full"` (TOML string): named method.
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
89#[serde(untagged)]
90pub enum VerifyLevel {
91    Bool(bool),
92    Method(VerifyMethod),
93}
94
95/// Named verification methods (the string form of [`VerifyLevel`]).
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
97#[serde(rename_all = "lowercase")]
98pub enum VerifyMethod {
99    Neural,
100    Test,
101    Full,
102}
103
104/// What syntactic region an annotation covers. Determines the body region
105/// hashed for staleness detection (per B3) and the scope a `"test"`-mode
106/// assertion is injected over.
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
108#[serde(rename_all = "snake_case")]
109pub enum CoveredRegion {
110    /// Whole function body (most common; α-form attribute on a fn).
111    Function,
112    /// A single statement (β-form macro before a stmt or loop).
113    Statement,
114    /// Every method in an `impl` block.
115    ImplMethods,
116    /// The inline body of a module item.
117    ModuleInlineBody,
118    /// A struct or enum item.
119    Type,
120    /// A struct field or enum variant.
121    Field,
122    /// The crate root.
123    Crate,
124    /// A trait declaration.
125    Trait,
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn annotation_kind_round_trips_lowercase() {
134        let v = serde_json::to_value(AnnotationKind::Intent).unwrap();
135        assert_eq!(v, serde_json::json!("intent"));
136        let back: AnnotationKind = serde_json::from_value(v).unwrap();
137        assert_eq!(back, AnnotationKind::Intent);
138    }
139
140    #[test]
141    fn status_pending_deepen_is_kebab_case() {
142        let v = serde_json::to_value(Status::PendingDeepen).unwrap();
143        assert_eq!(v, serde_json::json!("pending-deepen"));
144        let back: Status = serde_json::from_value(serde_json::json!("pending-deepen")).unwrap();
145        assert_eq!(back, Status::PendingDeepen);
146    }
147
148    #[test]
149    fn verify_level_accepts_bool_form() {
150        let level: VerifyLevel = serde_json::from_value(serde_json::json!(false)).unwrap();
151        assert_eq!(level, VerifyLevel::Bool(false));
152        let level: VerifyLevel = serde_json::from_value(serde_json::json!(true)).unwrap();
153        assert_eq!(level, VerifyLevel::Bool(true));
154    }
155
156    #[test]
157    fn verify_level_accepts_named_method_form() {
158        let level: VerifyLevel = serde_json::from_value(serde_json::json!("full")).unwrap();
159        assert_eq!(level, VerifyLevel::Method(VerifyMethod::Full));
160    }
161
162    #[test]
163    fn verify_level_rejects_unknown_string() {
164        let result: Result<VerifyLevel, _> = serde_json::from_value(serde_json::json!("partial"));
165        assert!(result.is_err());
166    }
167
168    #[test]
169    fn covered_region_uses_snake_case() {
170        let v = serde_json::to_value(CoveredRegion::ImplMethods).unwrap();
171        assert_eq!(v, serde_json::json!("impl_methods"));
172        let v = serde_json::to_value(CoveredRegion::ModuleInlineBody).unwrap();
173        assert_eq!(v, serde_json::json!("module_inline_body"));
174    }
175
176    #[test]
177    fn unknown_status_value_is_rejected() {
178        let result: Result<Status, _> = serde_json::from_value(serde_json::json!("partial"));
179        assert!(result.is_err());
180    }
181
182    #[test]
183    fn terminal_clean_is_exactly_verified_tested_neural() {
184        for s in [Status::Verified, Status::Tested, Status::Neural] {
185            assert!(s.is_terminal_clean(), "{s:?} should be terminal-clean");
186        }
187        for s in [
188            Status::Stale,
189            Status::Orphan,
190            Status::Forged,
191            Status::Unknown,
192            Status::PendingDeepen,
193            Status::Counterexample,
194            Status::Inconclusive,
195        ] {
196            assert!(!s.is_terminal_clean(), "{s:?} must NOT be terminal-clean");
197        }
198    }
199}