elicitation 0.8.0

Conversational elicitation of strongly-typed Rust values via MCP
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
//! Kani verification harnesses for formal contract verification.
//!
//! These harnesses verify that contract properties hold under symbolic execution.
//!
//! # Organization
//!
//! - **Primitive Contracts**: String, i32, bool
//! - **Complex Contracts**: Email, NonEmpty, etc.
//! - **Composition**: Multiple contracts together

#![cfg(kani)]

use crate::verification::Contract;
use crate::verification::contracts::{BoolValid, I32Positive, StringNonEmpty};

// ============================================================================
// Phase 1: Primitive Type Contracts
// ============================================================================

/// Verify StringNonEmpty contract with symbolic execution.
///
/// **Property:** Non-empty strings remain non-empty.
#[kani::proof]
fn verify_string_non_empty_contract() {
    // Create various concrete strings to test
    let inputs = [
        String::from("a"),
        String::from("hello"),
        String::from("test string"),
        String::from("x".repeat(50)),
    ];

    for input in inputs.iter() {
        // Assume precondition
        kani::assume(StringNonEmpty::requires(input));

        // Property 1: Input is non-empty
        assert!(!input.is_empty());
        assert!(input.len() > 0);

        // Property 2: Identity transformation preserves non-emptiness
        let output = input.clone();
        assert!(StringNonEmpty::ensures(input, &output));
        assert!(!output.is_empty());
    }
}

/// Verify I32Positive contract with symbolic execution.
///
/// **Property:** Positive numbers remain positive.
#[kani::proof]
fn verify_i32_positive_contract() {
    // Symbolic i32
    let input: i32 = kani::any();

    // Assume precondition
    kani::assume(I32Positive::requires(&input));

    // Property 1: Input is positive
    assert!(input > 0);

    // Property 2: Identity transformation preserves positivity
    let output = input;
    assert!(I32Positive::ensures(&input, &output));
    assert!(output > 0);
}

/// Verify BoolValid contract (trivial).
///
/// **Property:** All booleans are valid.
#[kani::proof]
fn verify_bool_valid_contract() {
    // Symbolic bool
    let input: bool = kani::any();

    // Property: All booleans satisfy precondition
    assert!(BoolValid::requires(&input));

    // Property: All transformations are valid
    let output = !input; // Even negation is valid
    assert!(BoolValid::ensures(&input, &output));
}

// ============================================================================
// Legacy Examples (for reference)
// ============================================================================

/// Contract that ensures output is non-empty when input is non-empty.
struct NonEmptyString;

impl Contract for NonEmptyString {
    type Input = String;
    type Output = String;

    fn requires(input: &String) -> bool {
        !input.is_empty()
    }

    fn ensures(_input: &String, output: &String) -> bool {
        !output.is_empty()
    }
}

/// Contract that validates email format.
struct ValidEmail;

impl Contract for ValidEmail {
    type Input = String;
    type Output = String;

    fn requires(input: &String) -> bool {
        input.contains('@') && input.len() > 2
    }

    fn ensures(_input: &String, output: &String) -> bool {
        output.contains('@')
    }
}

/// Contract for number validation (positive only).
struct PositiveNumber;

impl Contract for PositiveNumber {
    type Input = i32;
    type Output = i32;

    fn requires(input: &i32) -> bool {
        *input > 0
    }

    fn ensures(_input: &i32, output: &i32) -> bool {
        *output > 0
    }
}

// ============================================================================
// Kani Verification Harnesses
// ============================================================================

#[kani::proof]
fn verify_non_empty_string_contract() {
    // Symbolic string input
    let input = String::from("test");

    // Assume precondition
    kani::assume(NonEmptyString::requires(&input));

    // Property: precondition guarantees non-empty
    assert!(!input.is_empty());
}

#[kani::proof]
fn verify_email_requires_at_symbol() {
    // Symbolic string
    let input = String::from("user@example.com");

    // Assume precondition holds
    kani::assume(ValidEmail::requires(&input));

    // Property: valid emails must contain @
    assert!(input.contains('@'));
    assert!(input.len() > 2);
}

#[kani::proof]
fn verify_positive_number_contract() {
    // Symbolic integer
    let input: i32 = kani::any();

    // Assume precondition
    kani::assume(PositiveNumber::requires(&input));

    // Property: precondition guarantees positive
    assert!(input > 0);
}

#[kani::proof]
fn verify_contract_composition_preconditions() {
    // Verify that if Contract A's postcondition holds,
    // and Contract B's precondition requires what A ensures,
    // then composition is valid.

    let email = String::from("user@example.com");

    // Contract A (ValidEmail) postcondition
    kani::assume(ValidEmail::ensures(&email, &email));

    // Verify email still has @ (preserved property)
    assert!(email.contains('@'));

    // Contract B (NonEmptyString) can accept this
    assert!(NonEmptyString::requires(&email));
}

// ============================================================================
// Phase 4: Contract Primitives Verification (New System)
// ============================================================================

/// Verify that Established proofs are zero-sized.
///
/// **Property:** All proof markers compile away completely.
#[kani::proof]
fn verify_proof_zero_sized() {
    use crate::contracts::{Established, Is};

    let proof: Established<Is<String>> = Established::assert();
    assert_eq!(std::mem::size_of_val(&proof), 0);

    let proof_i32: Established<Is<i32>> = Established::assert();
    assert_eq!(std::mem::size_of_val(&proof_i32), 0);
}

/// Verify conjunction is commutative (at the logical level).
///
/// **Property:** both(p, q) contains both proofs.
#[kani::proof]
fn verify_conjunction_projects() {
    use crate::contracts::{Established, Is, both, fst, snd};

    let p: Established<Is<String>> = Established::assert();
    let q: Established<Is<i32>> = Established::assert();

    let pq = both(p, q);

    // Can project left
    let _p2: Established<Is<String>> = fst(pq);

    // Can project right
    let _q2: Established<Is<i32>> = snd(pq);
}

/// Verify reflexive implication works.
///
/// **Property:** Every proposition implies itself.
#[kani::proof]
fn verify_reflexive_implies() {
    use crate::contracts::{Established, Is};

    let proof: Established<Is<String>> = Established::assert();
    let same: Established<Is<String>> = proof.weaken();

    // Both are zero-sized
    assert_eq!(std::mem::size_of_val(&same), 0);
}

/// Verify True axiom is always available.
///
/// **Property:** True::axiom() never fails.
#[kani::proof]
fn verify_true_axiom() {
    use crate::tool::True;

    let _proof1 = True::axiom();
    let _proof2 = True::axiom();
    let _proof3 = True::axiom();

    // All are zero-sized
    let proof = True::axiom();
    assert_eq!(std::mem::size_of_val(&proof), 0);
}

/// Verify InVariant is zero-sized.
///
/// **Property:** Enum variant proofs compile away.
#[kani::proof]
fn verify_invariant_zero_sized() {
    use crate::contracts::{Established, InVariant};

    enum State {
        Active,
        Inactive,
    }
    struct ActiveVariant;

    let proof: Established<InVariant<State, ActiveVariant>> = Established::assert();
    assert_eq!(std::mem::size_of_val(&proof), 0);
}

/// Verify conjunction chaining works.
///
/// **Property:** Can nest conjunctions arbitrarily.
#[kani::proof]
fn verify_conjunction_associative() {
    use crate::contracts::{And, Established, Is, both};

    struct P;
    struct Q;
    struct R;
    impl crate::contracts::Prop for P {}
    impl crate::contracts::Prop for Q {}
    impl crate::contracts::Prop for R {}

    let p: Established<P> = Established::assert();
    let q: Established<Q> = Established::assert();
    let r: Established<R> = Established::assert();

    // Can nest: (P ∧ Q) ∧ R
    let pq = both(p, q);
    let _pqr: Established<And<And<P, Q>, R>> = both(pq, r);
}

// ============================================================================
// Phase 4.2: Tool Chain Verification
// ============================================================================

/// Verify that tools require precondition proofs.
///
/// **Property:** Cannot call tool without establishing precondition.
#[kani::proof]
fn verify_tool_requires_precondition() {
    use crate::contracts::{Established, Prop};
    use crate::tool::True;

    struct Validated;
    impl Prop for Validated {}

    // Mock tool that requires validation
    fn mock_tool(_input: String, _pre: Established<Validated>) -> (String, Established<True>) {
        // Tool implementation would validate
        ("output".to_string(), True::axiom())
    }

    // Must provide proof
    let proof: Established<Validated> = Established::assert();
    let (_result, _post) = mock_tool("input".to_string(), proof);

    // This would not compile without proof:
    // let (_result, _post) = mock_tool("input".to_string());  // ERROR!
}

/// Verify that True axiom is always available (no preconditions).
///
/// **Property:** True::axiom() can be called without any preconditions.
#[kani::proof]
fn verify_true_always_available() {
    use crate::tool::True;

    // Can create True proofs anytime
    let _proof1 = True::axiom();
    let _proof2 = True::axiom();

    // Mock unconstrained tool
    fn unconstrained_tool(_input: String, _pre: crate::contracts::Established<True>) -> String {
        "result".to_string()
    }

    let _result = unconstrained_tool("input".to_string(), True::axiom());
}

/// Verify tool composition maintains invariants.
///
/// **Property:** Chaining tools with `then()` preserves type safety.
#[kani::proof]
fn verify_tool_chain_composition() {
    use crate::contracts::{Established, Implies, Prop};
    use crate::tool::True;

    struct InputValid;
    struct OutputTransformed;
    impl Prop for InputValid {}
    impl Prop for OutputTransformed {}
    impl Implies<OutputTransformed> for InputValid {} // First's post implies second's pre

    // Tool 1: Validate input
    fn validate(_input: String, _pre: Established<True>) -> (String, Established<InputValid>) {
        ("validated".to_string(), Established::assert())
    }

    // Tool 2: Transform (requires validation)
    fn transform(
        _input: String,
        _pre: Established<OutputTransformed>,
    ) -> (String, Established<True>) {
        ("transformed".to_string(), True::axiom())
    }

    // Chain: validate then transform
    let (_validated, proof1) = validate("input".to_string(), True::axiom());
    let proof2: Established<OutputTransformed> = proof1.weaken(); // Post → Pre
    let (_result, _proof_final) = transform("validated".to_string(), proof2);

    // Type system enforces: cannot skip validation
}

/// Verify parallel composition maintains both contracts.
///
/// **Property:** both_tools() requires both preconditions, establishes both postconditions.
#[kani::proof]
fn verify_parallel_composition() {
    use crate::contracts::{And, Established, Prop, both, fst, snd};
    use crate::tool::True;

    struct EmailValidated;
    struct PhoneValidated;
    impl Prop for EmailValidated {}
    impl Prop for PhoneValidated {}

    // Tool 1: Validate email
    fn validate_email(
        _email: String,
        _pre: Established<True>,
    ) -> (String, Established<EmailValidated>) {
        ("email@example.com".to_string(), Established::assert())
    }

    // Tool 2: Validate phone
    fn validate_phone(
        _phone: String,
        _pre: Established<True>,
    ) -> (String, Established<PhoneValidated>) {
        ("+1234567890".to_string(), Established::assert())
    }

    // Must provide both preconditions
    let pre1 = True::axiom();
    let pre2 = True::axiom();
    let combined_pre = both(pre1, pre2);

    // Simulate both_tools behavior
    let p1 = fst(combined_pre);
    let p2 = snd(combined_pre);

    let (_email, email_proof) = validate_email("test@example.com".to_string(), p1);
    let (_phone, phone_proof) = validate_phone("1234567890".to_string(), p2);

    let _combined_post: Established<And<EmailValidated, PhoneValidated>> =
        both(email_proof, phone_proof);

    // Both postconditions established
}

/// Verify refinement downcast is sound.
///
/// **Property:** If Refined refines Base, then Is<Refined> implies Is<Base>.
#[kani::proof]
fn verify_refinement_soundness() {
    use crate::contracts::{Established, Implies, Is, Refines, downcast};

    struct NonEmptyString(String);
    impl Refines<String> for NonEmptyString {}
    impl Implies<Is<String>> for Is<NonEmptyString> {}

    // Have proof of refined type
    let refined_proof: Established<Is<NonEmptyString>> = Established::assert();

    // Can safely downcast to base
    let _base_proof: Established<Is<String>> = downcast(refined_proof);

    // Kani verifies this is safe (refinement preserves inhabitation)
}

/// Verify conjunction projection preserves properties.
///
/// **Property:** If (P ∧ Q) holds, then both P and Q hold.
#[kani::proof]
fn verify_conjunction_soundness() {
    use crate::contracts::{And, Established, Prop, both, fst, snd};

    struct P;
    struct Q;
    impl Prop for P {}
    impl Prop for Q {}

    let p: Established<P> = Established::assert();
    let q: Established<Q> = Established::assert();

    // Establish conjunction
    let pq: Established<And<P, Q>> = both(p, q);

    // Project left: P holds
    let _p_again: Established<P> = fst(pq);

    // Project right: Q holds
    let _q_again: Established<Q> = snd(pq);

    // Kani verifies projections are sound
}

/// Verify InVariant for enum state machines.
///
/// **Property:** Variant proofs enforce state machine transitions.
#[kani::proof]
fn verify_invariant_state_machine() {
    use crate::contracts::{Established, InVariant};

    enum State {
        Draft,
        Approved,
    }
    struct DraftVariant;
    struct ApprovedVariant;

    // State-specific function requires draft proof
    fn edit_draft(_state: State, _proof: Established<InVariant<State, DraftVariant>>) {
        // Can only call in Draft state
    }

    // Transition function returns new proof
    fn approve(
        _state: State,
        _draft: Established<InVariant<State, DraftVariant>>,
    ) -> Established<InVariant<State, ApprovedVariant>> {
        Established::assert()
    }

    let draft_proof: Established<InVariant<State, DraftVariant>> = Established::assert();
    edit_draft(State::Draft, draft_proof);

    let approved_proof = approve(State::Draft, draft_proof);
    let _final_state = (State::Approved, approved_proof);

    // Cannot call edit_draft with approved_proof (type error)
}