Skip to main content

elicitation/
contracts.rs

1//! Proof-carrying composition primitives.
2//!
3//! This module provides a minimal type-based contract system for building
4//! **formally verified** agent programs. Contracts are zero-cost proof markers that enable
5//! composing elicitation steps with machine-checked guarantees.
6//!
7//! # What Are Contracts?
8//!
9//! Contracts are **compile-time proof markers** that track guarantees through your program.
10//! Unlike runtime validation or testing, contracts provide **mathematical certainty** that
11//! invariants hold at every step of a multi-step workflow.
12//!
13//! ```text
14//! Traditional Approach         Contract Approach
15//! ==================          =================
16//! validate(x)                 validate(x) → (x, Proof)
17//! use(x)  // Hope valid       use(x, Proof)  // Type-checked!
18//! ```
19//!
20//! **Key insight**: Validate once, carry proof forward. The type system prevents using
21//! unvalidated data, and all proofs compile away to nothing.
22//!
23//! # Overview
24//!
25//! Contracts let you build multi-step agent workflows where each step's
26//! guarantees are **checked at compile time**. Instead of re-validating data
27//! at every step, you establish proof once and carry it forward.
28//!
29//! ## Comparison to Other Approaches
30//!
31//! | Approach | When Checked | Cost | Guarantees |
32//! |----------|-------------|------|------------|
33//! | **Runtime validation** | Every use | High | None (can forget) |
34//! | **Testing** | Test time | Medium | Statistical only |
35//! | **Static analysis** | Compile time | Low | Heuristic |
36//! | **Contracts (this)** | Compile time | **Zero** | **Mathematical** |
37//!
38//! ## Why Not Dependent Types?
39//!
40//! Dependent type systems (Idris, Agda, Coq) provide similar guarantees but:
41//! - Require theorem proving skills
42//! - Have steep learning curves
43//! - Don't integrate with existing Rust code
44//!
45//! Contracts give you **80% of the benefit with 5% of the complexity**.
46//!
47//! # Quick Start
48//!
49//! ```rust
50//! use elicitation::contracts::{Established, And, both};
51//!
52//! // Define your workflow's propositions
53//! #[derive(elicitation::Prop)]
54//! struct EmailValidated;
55//! #[derive(elicitation::Prop)]
56//! struct ConsentObtained;
57//!
58//! // Step 1: Validate (returns proof if valid)
59//! fn validate_email(email: &str) -> Option<Established<EmailValidated>> {
60//!     if email.contains('@') { Some(Established::assert()) } else { None }
61//! }
62//!
63//! // Step 2: Function requiring BOTH proofs
64//! fn register_user(
65//!     email: String,
66//!     _proof: Established<And<EmailValidated, ConsentObtained>>
67//! ) {
68//!     println!("Registered: {}", email);
69//! }
70//!
71//! // Compose: Can't call register_user without both proofs!
72//! # let email = "user@example.com";
73//! if let Some(email_proof) = validate_email(email) {
74//!     let consent_proof = Established::assert(); // Would come from consent flow
75//!     let combined = both(email_proof, consent_proof);
76//!     register_user(email.to_string(), combined); // ✅ Compiles
77//! }
78//! // register_user(email.to_string(), ...); // ❌ Won't compile without proof
79//! ```
80//!
81//! # Core Concepts
82//!
83//! - **Proposition (`Prop`)**: A type-level statement that can be true or false
84//! - **Proof (`Established<P>`)**: Evidence that proposition P holds
85//! - **Inhabitation (`Is<T>`)**: The proposition that a value inhabits type T
86//!
87//! # Example
88//!
89//! ```rust
90//! use elicitation::contracts::{Prop, Established, Is};
91//! use std::marker::PhantomData;
92//!
93//! // A proposition: value inhabits String
94//! type StringValid = Is<String>;
95//!
96//! // Function that requires proof
97//! fn use_validated_string(
98//!     s: String,
99//!     _proof: Established<StringValid>
100//! ) {
101//!     println!("Processing: {}", s);
102//! }
103//!
104//! // Establish proof after validation
105//! let s = String::from("hello");
106//! let proof = Established::assert();
107//! use_validated_string(s, proof);
108//! ```
109//!
110//! # Design Principles
111//!
112//! - **Zero runtime cost**: All proofs are `PhantomData` and disappear at compile time
113//! - **Minimal logic**: Just what's needed for composition (no quantifiers, no negation)
114//! - **Type-safe composition**: Cannot call functions without required proofs
115//! - **Monotonic refinement**: Guarantees accumulate, never weaken unexpectedly
116//!
117//! # When to Use
118//!
119//! Use contracts when:
120//! - Building multi-step agent flows with dependencies between steps
121//! - Enforcing preconditions that must be established by prior steps
122//! - Verifying that validation happens before use (no re-validation needed)
123//!
124//! Don't use contracts when:
125//! - Single-step elicitation (just use `.elicit()` directly)
126//! - No dependencies between steps
127//! - Performance is so critical you can't afford any abstraction (though cost is zero!)
128//!
129//! # Multi-Step Composition Example
130//!
131//! ```rust
132//! use elicitation::contracts::{Established, And, both, Is};
133//!
134//! // Define propositions for agent workflow
135//! #[derive(elicitation::Prop)]
136//! struct EmailValidated;
137//! #[derive(elicitation::Prop)]
138//! struct ConsentObtained;
139//!
140//! // Step 1: Validate email (establishes EmailValidated)
141//! fn validate_email(email: &str) -> Option<Established<EmailValidated>> {
142//!     if email.contains('@') {
143//!         Some(Established::assert())
144//!     } else {
145//!         None
146//!     }
147//! }
148//!
149//! // Step 2: Get consent (establishes ConsentObtained)
150//! fn get_consent(user: &str) -> Established<ConsentObtained> {
151//!     println!("Getting consent from {}", user);
152//!     Established::assert()
153//! }
154//!
155//! // Step 3: Register user (requires BOTH proofs)
156//! fn register_user(
157//!     email: String,
158//!     _proof: Established<And<EmailValidated, ConsentObtained>>
159//! ) {
160//!     println!("Registered: {}", email);
161//! }
162//!
163//! // Compose the workflow
164//! let email = "user@example.com";
165//! if let Some(email_proof) = validate_email(email) {
166//!     let consent_proof = get_consent(email);
167//!     let combined_proof = both(email_proof, consent_proof);
168//!     register_user(email.to_string(), combined_proof);
169//! }
170//! ```
171//!
172//! # API Overview
173//!
174//! ## Core Types
175//!
176//! - [`Prop`]: Marker trait for propositions (type-level statements)
177//! - [`Established<P>`]: Proof that proposition P holds
178//! - [`Is<T>`]: Proposition that a value inhabits type T
179//!
180//! ## Logical Operators
181//!
182//! - [`And<P, Q>`][]: Conjunction (both P and Q hold)
183//! - [`Implies<Q>`][]: Implication (P → Q)
184//! - [`Refines<Base>`][]: Type refinement (Refined is a Base with extra constraints)
185//! - [`InVariant<E, V>`][]: Enum is in specific variant
186//!
187//! ## Composition Functions
188//!
189//! - [`both(p, q)`](crate::both): Combine two proofs into conjunction
190//! - `fst(pq)` — Project left proof from conjunction
191//! - `snd(pq)` — Project right proof from conjunction
192//! - `downcast(refined)` — Safe downcast from refined type to base
193//!
194//! # Advanced Patterns
195//!
196//! ## State Machines
197//!
198//! Use `InVariant` to enforce state transitions:
199//!
200//! ```rust
201//! use elicitation::contracts::{Established, InVariant};
202//!
203//! enum Workflow { Draft, Review, Approved }
204//! struct DraftVariant;
205//! struct ReviewVariant;
206//!
207//! fn submit(
208//!     _workflow: Workflow,
209//!     _draft: Established<InVariant<Workflow, DraftVariant>>
210//! ) -> Established<InVariant<Workflow, ReviewVariant>> {
211//!     Established::assert()
212//! }
213//!
214//! // Can only submit from Draft state (type-checked!)
215//! ```
216//!
217//! ## Type Refinement
218//!
219//! Use `Refines` for type hierarchies. Note: both traits must be implemented
220//! in your crate to satisfy orphan rules:
221//!
222//! ```rust,ignore
223//! use elicitation::contracts::{Refines, Is, Established, Implies, downcast};
224//!
225//! struct NonEmptyString(String);
226//! impl Refines<String> for NonEmptyString {}
227//! impl Implies<Is<String>> for Is<NonEmptyString> {}
228//!
229//! let refined: Established<Is<NonEmptyString>> = Established::assert();
230//! let base: Established<Is<String>> = downcast(refined);
231//! // Safe: NonEmptyString is always a String
232//! ```
233//!
234//! # Integration with Elicitation
235//!
236//! Use `elicit_proven()` to get values with proofs:
237//!
238//! ```rust,ignore
239//! use elicitation::{Elicitation, contracts::{Established, Is}};
240//!
241//! // Elicit with proof
242//! let (email, proof): (String, Established<Is<String>>) =
243//!     String::elicit_proven(&client).await?;
244//!
245//! // Pass proof to functions requiring validation
246//! send_email(email, proof).await?;
247//! ```
248//!
249//! # Migration Guide
250//!
251//! Contracts are **100% opt-in** and don't affect existing code:
252//!
253//! ```text
254//! Before (still works):
255//!   let email = String::elicit(&client).await?;
256//!   use_email(email);
257//!
258//! After (with contracts):
259//!   let (email, proof) = String::elicit_proven(&client).await?;
260//!   use_email_proven(email, proof);
261//! ```
262//!
263//! You can adopt incrementally:
264//! 1. Start with single-step validation
265//! 2. Add contracts to critical paths
266//! 3. Extend to full workflows over time
267//!
268//! # Performance
269//!
270//! **Zero cost at runtime**: All proofs are `PhantomData<fn() -> T>` and compile away completely.
271//!
272//! ```rust
273//! use elicitation::contracts::{Established, Is};
274//!
275//! let proof: Established<Is<String>> = Established::assert();
276//! assert_eq!(std::mem::size_of_val(&proof), 0); // Zero bytes!
277//! ```
278//!
279//! Benchmarks show no measurable overhead compared to unvalidated code.
280//!
281//! # Formal Verification
282//!
283//! All core properties are **formally verified with Kani**:
284//!
285//! - ✅ Proofs are zero-sized (verified with symbolic execution)
286//! - ✅ Cannot call functions without required proofs (type system + Kani)
287//! - ✅ Composition preserves invariants (Kani proves `then()` and `both_tools()`)
288//! - ✅ Refinement is sound (Kani proves `downcast()` safety)
289//!
290//! See `src/kani_tests.rs` for complete verification harnesses.
291//!
292//! # When NOT to Use Contracts
293//!
294//! - **Single-step operations**: Just use regular functions
295//! - **External APIs**: You can't force external code to use proofs
296//! - **Prototyping**: Add contracts after the design stabilizes
297//! - **Performance-critical inner loops**: (Though cost is zero, type complexity adds compile time)
298//!
299//! # Further Reading
300//!
301//! - [Tool contracts](crate::tool): MCP tools with preconditions/postconditions
302//! - [Examples](../../examples): Complete working examples
303//! - [Kani verification](https://model-checking.github.io/kani/): How we verify properties
304
305use proc_macro2;
306use std::marker::PhantomData;
307
308/// Marker trait: types that represent propositions.
309///
310/// A proposition is a type-level statement that can be true or false.
311/// Propositions are combined using logical operators (`And`, `Implies`)
312/// and witnessed by `Established<P>` proofs.
313///
314/// # Examples
315///
316/// ```rust
317/// use elicitation::contracts::{Prop, Is};
318///
319/// // Built-in proposition: value inhabits type T
320/// type StringProp = Is<String>;
321/// ```
322pub trait Prop: 'static {
323    /// Generate a Kani proof harness for this proposition.
324    ///
325    /// Returns a [`proc_macro2::TokenStream`] containing a `#[kani::proof]` harness
326    /// that encodes this proposition as a trusted axiom. There is no default —
327    /// every proposition must supply a proof. Use `#[derive(Prop)]` for trivial
328    /// zero-cost marker propositions.
329    ///
330    /// Available with the `proofs` feature.
331    fn kani_proof() -> proc_macro2::TokenStream;
332
333    /// Generate a Verus proof for this proposition.
334    ///
335    /// Returns a [`proc_macro2::TokenStream`] containing a Verus-verified function
336    /// encoding this proposition's postcondition invariant. There is no default —
337    /// use `#[derive(Prop)]` for trivial marker propositions.
338    ///
339    /// Available with the `proofs` feature.
340    fn verus_proof() -> proc_macro2::TokenStream;
341
342    /// Generate a Creusot contract proof for this proposition.
343    ///
344    /// Returns a [`proc_macro2::TokenStream`] containing a `#[trusted]` Creusot
345    /// contract function encoding this proposition's postcondition. There is no
346    /// default — use `#[derive(Prop)]` for trivial marker propositions.
347    ///
348    /// Available with the `proofs` feature.
349    fn creusot_proof() -> proc_macro2::TokenStream;
350
351    /// The name of the Creusot pearlite `#[logic]` function that expresses this
352    /// proposition as a predicate over the machine state.
353    ///
354    /// When non-empty, `#[formal_method]`-generated Creusot companions emit
355    /// real `#[requires]` / `#[ensures]` contracts using this function instead
356    /// of the weaker `#[trusted]` placeholder.
357    ///
358    /// Set via `#[prop(creusot_invariant_fn = "my_fn")]` on the prop struct.
359    /// The named function must be a `#[cfg(creusot)] #[logic]` function in scope
360    /// wherever the generated companions are compiled.
361    fn creusot_invariant_fn_name() -> &'static str {
362        ""
363    }
364
365    /// The name of a `#[cfg(kani)]` boolean predicate function that expresses
366    /// this proposition as a Kani-evaluable invariant check.
367    ///
368    /// When non-empty, [`formal_method`][crate::formal_method]-generated companion
369    /// structs emit `#[kani::requires(fn(&state))]` / `#[kani::ensures]` contracted
370    /// wrappers, enabling `#[kani::proof_for_contract]` closure proofs that close
371    /// the depth-based inductive argument into a full symbolic proof.
372    ///
373    /// Set via `#[prop(kani_invariant_fn = "my_fn")]` on the prop struct.
374    fn kani_invariant_fn_name() -> &'static str {
375        ""
376    }
377
378    /// The name of a Verus `pub open spec fn` that expresses this proposition
379    /// as a Verus-verifiable predicate over the machine state.
380    ///
381    /// When non-empty, [`formal_method`][crate::formal_method]-generated companion
382    /// structs emit `assume_specification` blocks with real `requires`/`ensures`
383    /// clauses using this function, enabling Verus to check invariant preservation.
384    ///
385    /// Set via `#[prop(verus_invariant_fn = "my_fn")]` on the prop struct.
386    fn verus_invariant_fn_name() -> &'static str {
387        ""
388    }
389}
390
391/// Witness that proposition P has been established.
392///
393/// This is a zero-sized proof marker. Its existence proves that
394/// proposition P holds in the current context.
395///
396/// # Zero Cost
397///
398/// ```rust
399/// use elicitation::contracts::{Established, Is};
400/// use std::mem::size_of;
401///
402/// let proof: Established<Is<String>> = Established::assert();
403/// assert_eq!(size_of::<Established<Is<String>>>(), 0);
404/// ```
405///
406/// # Safety Model
407///
408/// `Established<P>` is a semantic contract, not a memory safety guarantee.
409/// Calling `Established::assert()` asserts that P is true. The type system
410/// ensures you cannot call functions requiring proof without providing it,
411/// but it's your responsibility to only assert when P actually holds.
412pub struct Established<P: Prop> {
413    _marker: PhantomData<fn() -> P>,
414}
415
416/// Relation: proposition `P` can be proven from credential `C`.
417///
418/// Implement this on a `Prop` type to declare which credentials can mint it.
419/// The only way to construct `Established<P>` externally is via
420/// `Established::<P>::prove(&credential)` where `P: ProvableFrom<C>`.
421pub trait ProvableFrom<C>: Prop {}
422
423/// Declare one or more proof-credential ZST types and wire each to its proposition.
424///
425/// # Syntax
426///
427/// ```text
428/// proof_credential! {
429///     /// Optional doc comment.
430///     VISIBILITY CredentialName => PropositionType;
431///     ...
432/// }
433/// ```
434///
435/// Each entry emits:
436/// 1. A zero-sized struct `CredentialName` with the given visibility and doc comments.
437/// 2. `impl ProvableFrom<CredentialName> for PropositionType {}`
438///
439/// Credentials are typically `pub(crate)` so only the factory method that
440/// performed the runtime check can construct them.  External code cannot call
441/// `Established::prove(&CredentialName)` without access to the type.
442///
443/// # Example
444///
445/// ```rust
446/// use elicitation::contracts::{Established, Prop, ProvableFrom};
447/// use elicitation::proof_credential;
448///
449/// #[derive(elicitation::Prop)]
450/// pub struct ContrastChecked;
451///
452/// proof_credential! {
453///     /// Witness that a colour pair was verified for WCAG contrast.
454///     pub(crate) NormalContrastVerified => ContrastChecked;
455/// }
456///
457/// // Inside the factory (same crate):
458/// let proof: Established<ContrastChecked> =
459///     Established::prove(&NormalContrastVerified);
460/// ```
461#[macro_export]
462macro_rules! proof_credential {
463    (
464        $(
465            $(#[$meta:meta])*
466            $vis:vis $cred:ident => $prop:ty;
467        )*
468    ) => {
469        $(
470            $(#[$meta])*
471            $vis struct $cred;
472
473            impl $crate::contracts::ProvableFrom<$cred> for $prop {}
474        )*
475    };
476}
477
478impl<P: Prop> Established<P> {
479    /// Mint a proof token by presenting a valid credential.
480    ///
481    /// The credential type `C` must implement `ProvableFrom<C>` for `P`,
482    /// establishing a declared relationship between the two.
483    ///
484    /// # Examples
485    ///
486    /// ```rust
487    /// use elicitation::contracts::{Established, Is, Prop, ProvableFrom};
488    ///
489    /// #[derive(elicitation::Prop)]
490    /// struct InputValidated;
491    ///
492    /// struct ValidInput(String);
493    /// impl ProvableFrom<ValidInput> for InputValidated {}
494    ///
495    /// let input = ValidInput("hello".to_string());
496    /// let proof: Established<InputValidated> = Established::prove(&input);
497    /// ```
498    #[inline(always)]
499    pub fn prove<C>(_credential: &C) -> Self
500    where
501        P: ProvableFrom<C>,
502    {
503        Self {
504            _marker: PhantomData,
505        }
506    }
507
508    /// Assert that proposition `P` holds without a credential.
509    ///
510    /// This is the unchecked escape hatch: callers take responsibility for
511    /// ensuring `P` genuinely holds.  Prefer
512    /// [`prove`][Established::prove] when a credential type exists, since
513    /// `prove` encodes the check in the type system.
514    ///
515    /// WCAG proposition types are protected by their credential types being
516    /// `pub(crate)` within `elicit_ui` — even with `assert()` available,
517    /// external code cannot construct the required credential to call
518    /// `prove()`, and calling `assert()` on a WCAG type directly is a clear
519    /// audit-trail violation.
520    #[inline(always)]
521    pub fn assert() -> Self {
522        Self {
523            _marker: PhantomData,
524        }
525    }
526
527    /// Weaken proof to a more general proposition.
528    ///
529    /// If P implies Q, then a proof of P can be used as a proof of Q.
530    /// This is logical weakening - moving from a stronger to a weaker claim.
531    ///
532    /// # Examples
533    ///
534    /// ```rust
535    /// use elicitation::contracts::{Established, Is, Implies};
536    /// use std::marker::PhantomData;
537    ///
538    /// // StringNonEmpty implies String (via refinement)
539    /// #[derive(elicitation::Prop)]
540    /// struct StringNonEmpty;
541    /// impl Implies<Is<String>> for StringNonEmpty {}
542    ///
543    /// let strong_proof: Established<StringNonEmpty> = Established::assert();
544    /// let weak_proof: Established<Is<String>> = strong_proof.weaken();
545    /// ```
546    #[inline(always)]
547    pub fn weaken<Q: Prop>(self) -> Established<Q>
548    where
549        P: Implies<Q>,
550    {
551        Established {
552            _marker: PhantomData,
553        }
554    }
555}
556
557// Make Established copyable (it's zero-sized)
558impl<P: Prop> Copy for Established<P> {}
559impl<P: Prop> Clone for Established<P> {
560    fn clone(&self) -> Self {
561        *self
562    }
563}
564
565// Under Kani, `stub_verified` replaces a call with `kani::any()` of the
566// return type.  `Established<P>` is a ZST — its unique value is trivially
567// constructible, so any() just returns the unit token.
568#[cfg(kani)]
569impl<P: Prop> kani::Arbitrary for Established<P> {
570    fn any() -> Self {
571        Self {
572            _marker: std::marker::PhantomData,
573        }
574    }
575}
576
577impl<P: Prop> std::fmt::Debug for Established<P> {
578    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
579        write!(f, "Established")
580    }
581}
582
583impl<P: Prop + crate::Elicitation> crate::Prompt for Established<P> {
584    fn prompt() -> Option<&'static str> {
585        P::prompt()
586    }
587}
588
589impl<P: Prop + crate::Elicitation + Send> crate::Elicitation for Established<P> {
590    type Style = <P as crate::Elicitation>::Style;
591
592    #[tracing::instrument(skip(communicator))]
593    async fn elicit<C: crate::ElicitCommunicator>(communicator: &C) -> crate::ElicitResult<Self> {
594        tracing::debug!("Eliciting proof proposition for Established");
595        let _p = P::elicit(communicator).await?;
596        Ok(Established::assert())
597    }
598
599    fn kani_proof() -> proc_macro2::TokenStream {
600        proc_macro2::TokenStream::new()
601    }
602
603    fn verus_proof() -> proc_macro2::TokenStream {
604        proc_macro2::TokenStream::new()
605    }
606
607    fn creusot_proof() -> proc_macro2::TokenStream {
608        proc_macro2::TokenStream::new()
609    }
610}
611
612/// `Established<P>` is a zero-sized proof token — re-asserting it in emitted
613/// code is safe because the token carries no runtime state. The emitted call
614/// `Established::<P>::assert()` is identical to the original construction.
615impl<P: Prop + 'static> crate::emit_code::ToCodeLiteral for Established<P> {
616    fn type_tokens() -> proc_macro2::TokenStream {
617        let p: proc_macro2::TokenStream = std::any::type_name::<P>()
618            .parse()
619            .unwrap_or_else(|_| quote::quote! { _ });
620        quote::quote! { ::elicitation::Established<#p> }
621    }
622
623    fn to_code_literal(&self) -> proc_macro2::TokenStream {
624        let ty = <Self as crate::emit_code::ToCodeLiteral>::type_tokens();
625        quote::quote! { <#ty>::assert() }
626    }
627}
628
629/// `Established<P>` is a zero-sized proof token — it carries no user-facing
630/// prompt. Any struct that holds one as a field still needs the bound satisfied
631/// when `prompt-tree` is enabled; we return an empty `Leaf` so the derive
632/// can compile without noise in the assembled prompt output.
633impl<P: Prop> crate::ElicitPromptTree for Established<P> {
634    fn prompt_tree() -> crate::PromptTree {
635        crate::PromptTree::Leaf {
636            prompt: String::new(),
637            type_name: "Established".to_string(),
638        }
639    }
640}
641
642/// `Established<P>` is a zero-sized proof token. Serialization is trivial —
643/// the token carries no runtime state, so it round-trips through any format
644/// as a unit value. Deserializing reconstructs the token via `assert()`,
645/// which is safe because the surrounding serialized state already encodes the
646/// invariant that P holds (e.g. the bet balance is present in the ledger).
647#[cfg(feature = "serde")]
648impl<P: Prop> serde::Serialize for Established<P> {
649    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
650        serializer.serialize_unit()
651    }
652}
653
654#[cfg(feature = "serde")]
655impl<'de, P: Prop> serde::Deserialize<'de> for Established<P> {
656    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
657        <()>::deserialize(deserializer)?;
658        Ok(Self::assert())
659    }
660}
661
662impl<P: Prop + 'static> schemars::JsonSchema for Established<P> {
663    fn schema_name() -> std::borrow::Cow<'static, str> {
664        "EstablishedProof".into()
665    }
666
667    fn json_schema(_gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
668        schemars::json_schema!({ "type": "null" })
669    }
670}
671
672/// Proposition: value inhabits type T with its invariants.
673///
674/// `Is<T>` represents the statement "a value of type T exists and
675/// satisfies T's type invariants". For contract types (like `StringNonEmpty`),
676/// this includes the contract's preconditions.
677///
678/// # Examples
679///
680/// ```rust
681/// use elicitation::contracts::Is;
682///
683/// // Proposition: a valid String exists
684/// type StringValid = Is<String>;
685///
686/// // Proposition: a non-empty String exists (with contract)
687/// // type StringNonEmptyValid = Is<StringNonEmpty>;
688/// ```
689pub struct Is<T> {
690    _marker: PhantomData<fn() -> T>,
691}
692
693impl<T: 'static> Prop for Is<T> {
694    fn kani_proof() -> proc_macro2::TokenStream {
695        proc_macro2::TokenStream::new()
696    }
697
698    fn verus_proof() -> proc_macro2::TokenStream {
699        proc_macro2::TokenStream::new()
700    }
701
702    fn creusot_proof() -> proc_macro2::TokenStream {
703        proc_macro2::TokenStream::new()
704    }
705}
706
707/// Logical implication: P implies Q.
708///
709/// If P implies Q, then whenever P is true, Q must also be true.
710/// This enables weakening: converting a proof of P into a proof of Q.
711///
712/// # Laws
713///
714/// 1. **Reflexivity**: Every proposition implies itself (P → P)
715/// 2. **Transitivity**: If P → Q and Q → R, then P → R
716///
717/// # Examples
718///
719/// ```rust
720/// use elicitation::contracts::Implies;
721///
722/// #[derive(elicitation::Prop)]
723/// struct Strong;
724/// #[derive(elicitation::Prop)]
725/// struct Weak;
726///
727/// // Declare that Strong implies Weak
728/// impl Implies<Weak> for Strong {}
729/// ```
730pub trait Implies<Q: Prop>: Prop {}
731
732// Reflexivity: Every proposition implies itself
733impl<P: Prop> Implies<P> for P {}
734
735/// Type alias to reduce PhantomData complexity.
736type AndMarker<P, Q> = (fn() -> P, fn() -> Q);
737
738/// Logical conjunction: both P and Q hold.
739///
740/// `And<P, Q>` represents the proposition that both P and Q are true.
741/// This enables combining multiple proofs into a single compound proof.
742///
743/// # Properties
744///
745/// - **Commutative** (logically): P ∧ Q ≡ Q ∧ P
746/// - **Associative** (logically): (P ∧ Q) ∧ R ≡ P ∧ (Q ∧ R)
747/// - **Projectable**: And<P, Q> → P and And<P, Q> → Q
748///
749/// # Examples
750///
751/// ```rust
752/// use elicitation::contracts::{Established, And, both, fst, snd};
753/// use std::marker::PhantomData;
754///
755/// // Two propositions
756/// #[derive(elicitation::Prop)]
757/// struct ValidUrl;
758/// #[derive(elicitation::Prop)]
759/// struct HasPort;
760///
761/// let url_proof: Established<ValidUrl> = Established::assert();
762/// let port_proof: Established<HasPort> = Established::assert();
763///
764/// // Combine into conjunction
765/// let both_proof: Established<And<ValidUrl, HasPort>> = both(url_proof, port_proof);
766///
767/// // Project back out
768/// let url_again: Established<ValidUrl> = fst(both_proof);
769/// let port_again: Established<HasPort> = snd(both_proof);
770/// ```
771pub struct And<P: Prop, Q: Prop> {
772    _marker: PhantomData<AndMarker<P, Q>>,
773}
774
775impl<P: Prop, Q: Prop> Prop for And<P, Q> {
776    fn kani_proof() -> proc_macro2::TokenStream {
777        let mut ts = P::kani_proof();
778        ts.extend(Q::kani_proof());
779        ts
780    }
781
782    fn verus_proof() -> proc_macro2::TokenStream {
783        let mut ts = P::verus_proof();
784        ts.extend(Q::verus_proof());
785        ts
786    }
787
788    fn creusot_proof() -> proc_macro2::TokenStream {
789        let mut ts = P::creusot_proof();
790        ts.extend(Q::creusot_proof());
791        ts
792    }
793}
794
795/// Combine two proofs into a conjunction.
796///
797/// Given proofs of P and Q, construct a proof that both hold.
798///
799/// # Examples
800///
801/// ```rust
802/// use elicitation::contracts::{Established, And, both};
803///
804/// #[derive(elicitation::Prop)]
805/// struct P;
806/// #[derive(elicitation::Prop)]
807/// struct Q;
808///
809/// let p: Established<P> = Established::assert();
810/// let q: Established<Q> = Established::assert();
811/// let pq: Established<And<P, Q>> = both(p, q);
812/// ```
813#[inline(always)]
814pub fn both<P: Prop, Q: Prop>(_p: Established<P>, _q: Established<Q>) -> Established<And<P, Q>> {
815    Established {
816        _marker: PhantomData,
817    }
818}
819
820/// Project left component from conjunction.
821///
822/// Given a proof that both P and Q hold, extract a proof of P.
823///
824/// # Examples
825///
826/// ```rust
827/// use elicitation::contracts::{Established, And, both, fst};
828///
829/// #[derive(elicitation::Prop)]
830/// struct P;
831/// #[derive(elicitation::Prop)]
832/// struct Q;
833///
834/// let pq: Established<And<P, Q>> = both(Established::assert(), Established::assert());
835/// let p: Established<P> = fst(pq);
836/// ```
837#[inline(always)]
838pub fn fst<P: Prop, Q: Prop>(_both: Established<And<P, Q>>) -> Established<P> {
839    Established {
840        _marker: PhantomData,
841    }
842}
843
844/// Project right component from conjunction.
845///
846/// Given a proof that both P and Q hold, extract a proof of Q.
847///
848/// # Examples
849///
850/// ```rust
851/// use elicitation::contracts::{Established, And, both, snd};
852///
853/// #[derive(elicitation::Prop)]
854/// struct P;
855/// #[derive(elicitation::Prop)]
856/// struct Q;
857///
858/// let pq: Established<And<P, Q>> = both(Established::assert(), Established::assert());
859/// let q: Established<Q> = snd(pq);
860/// ```
861#[inline(always)]
862pub fn snd<P: Prop, Q: Prop>(_both: Established<And<P, Q>>) -> Established<Q> {
863    Established {
864        _marker: PhantomData,
865    }
866}
867
868/// Type-level refinement: `Self` refines `Base`.
869///
870/// A refinement type has all the properties of its base type, plus
871/// additional constraints. This means:
872/// - If a value inhabits the refined type, it necessarily inhabits the base type
873/// - You can safely downcast from refined proof to base proof
874/// - You cannot upcast (compile error without additional validation)
875///
876/// # Laws
877///
878/// 1. **Reflexivity**: Every type refines itself (T → T)
879/// 2. **Transitivity**: If A refines B and B refines C, then A refines C
880/// 3. **Inhabitation**: `Is<Refined>` implies `Is<Base>` (requires explicit `Implies` impl)
881///
882/// # Example
883///
884/// ```rust,no_run
885/// use elicitation::contracts::{Refines, Is, Established, Implies, Prop, downcast};
886///
887/// // Define a refined string type (in practice, would have validation)
888/// struct NonEmptyString(String);
889///
890/// // Declare refinement relationship and implication
891/// impl Refines<String> for NonEmptyString {}
892/// // In actual code: impl Implies<Is<String>> for Is<NonEmptyString> {}
893///
894/// // Can downcast from refined to base:
895/// // let refined_proof: Established<Is<NonEmptyString>> = Established::assert();
896/// // let base_proof: Established<Is<String>> = downcast(refined_proof);
897/// ```
898pub trait Refines<Base>: 'static {}
899
900// Reflexivity: every type refines itself
901impl<T: 'static> Refines<T> for T {}
902
903/// Downcast proof from refined type to base type.
904///
905/// If you have a proof that a value inhabits a refined type,
906/// you automatically have proof it inhabits the base type.
907///
908/// This works via the `Refines` trait relationship. Users must
909/// implement `Implies<Is<Base>>` for `Is<Refined>` to enable downcasting.
910///
911/// # Examples
912///
913/// ```rust,no_run
914/// use elicitation::contracts::{Refines, Is, Established, Implies, Prop, downcast};
915///
916/// struct NonEmptyString(String);
917/// impl Refines<String> for NonEmptyString {}
918///
919/// // In actual code, you'd implement this in your crate:
920/// // impl Implies<Is<String>> for Is<NonEmptyString> {}
921///
922/// // Then downcast works:
923/// // let refined: Established<Is<NonEmptyString>> = Established::assert();
924/// // let base: Established<Is<String>> = downcast(refined);
925/// ```
926#[inline(always)]
927pub fn downcast<Base: 'static, Refined: Refines<Base>>(
928    proof: Established<Is<Refined>>,
929) -> Established<Is<Base>>
930where
931    Is<Refined>: Implies<Is<Base>>,
932{
933    proof.weaken()
934}
935
936/// Type alias to reduce PhantomData complexity.
937type InVariantMarker<E, V> = (fn() -> E, fn() -> V);
938
939/// Proposition: enum value is in specific variant.
940///
941/// `InVariant<E, V>` represents the statement "enum E is currently
942/// in variant V". This enables variant-specific proofs for enum-based
943/// state machines.
944///
945/// # Type Parameters
946///
947/// - `E`: The enum type
948/// - `V`: A marker type representing the variant (typically a unit struct)
949///
950/// # Example
951///
952/// ```rust
953/// use elicitation::contracts::{InVariant, Established, Prop};
954///
955/// enum Status {
956///     Active,
957///     Inactive,
958/// }
959///
960/// // Marker types for variants
961/// struct ActiveVariant;
962/// struct InactiveVariant;
963///
964/// // Use InVariant to track which variant
965/// fn process_active(_status: Status, _proof: Established<InVariant<Status, ActiveVariant>>) {
966///     // This function can only be called with Active variant
967///     println!("Processing active status");
968/// }
969/// ```
970pub struct InVariant<E, V> {
971    _marker: PhantomData<InVariantMarker<E, V>>,
972}
973
974impl<E: 'static, V: 'static> Prop for InVariant<E, V> {
975    fn kani_proof() -> proc_macro2::TokenStream {
976        proc_macro2::TokenStream::new()
977    }
978
979    fn verus_proof() -> proc_macro2::TokenStream {
980        proc_macro2::TokenStream::new()
981    }
982
983    fn creusot_proof() -> proc_macro2::TokenStream {
984        proc_macro2::TokenStream::new()
985    }
986}
987
988// ── Formal Methods ────────────────────────────────────────────────────────────
989
990/// A function that consumes a proof and produces a proof.
991///
992/// Any `Fn(In, Established<PIn>) -> (Out, Established<POut>)` automatically
993/// implements this trait via the blanket impl below. The signature IS the
994/// contract declaration — no attribute or registry required.
995///
996/// # Type-driven call-graph closure
997///
998/// A function calling an informal helper cannot derive `Established<POut>` from
999/// that helper's result without using [`Established::assert`], the explicit,
1000/// auditable escape hatch. The type system therefore closes the call graph
1001/// automatically: proof tokens only flow from formal call sites.
1002///
1003/// # Evidence bundles
1004///
1005/// When multiple propositions must be satisfied, use an evidence bundle struct
1006/// (a plain struct whose fields are `Established<P>` tokens that implements
1007/// `ProvableFrom<Bundle>`). Pass the bundle as `In` or as `PIn` evidence.
1008///
1009/// # Examples
1010///
1011/// ```rust
1012/// use elicitation::contracts::{Established, FormalMethod, Prop};
1013///
1014/// #[derive(elicitation::Prop)]
1015/// struct Validated;
1016/// #[derive(elicitation::Prop)]
1017/// struct Processed;
1018///
1019/// // Any function with the matching signature is automatically a FormalMethod.
1020/// fn process(input: String, _proof: Established<Validated>)
1021///     -> (String, Established<Processed>)
1022/// {
1023///     (input.to_uppercase(), Established::assert())
1024/// }
1025///
1026/// // Use via the trait or call directly — both work.
1027/// let proof_in = Established::assert();
1028/// let (_result, _proof_out) = process("hello".to_string(), proof_in);
1029/// ```
1030pub trait FormalMethod<In, PIn: Prop, Out, POut: Prop> {
1031    /// Call this method, consuming the input proof and producing an output proof.
1032    fn call_formal(&self, input: In, proof: Established<PIn>) -> (Out, Established<POut>);
1033
1034    /// Generate a Kani proof harness for this method's precondition/postcondition.
1035    ///
1036    /// The default returns an empty token stream (no harness). Override to emit
1037    /// a `#[kani::proof]` harness asserting that the method honours its contract.
1038    fn kani_harness() -> proc_macro2::TokenStream {
1039        proc_macro2::TokenStream::new()
1040    }
1041
1042    /// Generate a Verus proof harness for this method's contract.
1043    fn verus_harness() -> proc_macro2::TokenStream {
1044        proc_macro2::TokenStream::new()
1045    }
1046
1047    /// Generate a Creusot contract for this method.
1048    fn creusot_harness() -> proc_macro2::TokenStream {
1049        proc_macro2::TokenStream::new()
1050    }
1051}
1052
1053/// Blanket impl: any `Fn(In, Established<PIn>) -> (Out, Established<POut>)`
1054/// is automatically a `FormalMethod`.
1055impl<F, In, PIn: Prop, Out, POut: Prop> FormalMethod<In, PIn, Out, POut> for F
1056where
1057    F: Fn(In, Established<PIn>) -> (Out, Established<POut>),
1058{
1059    #[inline(always)]
1060    fn call_formal(&self, input: In, proof: Established<PIn>) -> (Out, Established<POut>) {
1061        self(input, proof)
1062    }
1063}
1064
1065// ── Verified State Machines ───────────────────────────────────────────────────
1066
1067/// A state machine whose states are fully described and whose transitions
1068/// preserve a declared invariant.
1069///
1070/// # Contract
1071///
1072/// A `VerifiedStateMachine` declares two associated types:
1073///
1074/// - `State` — must be [`ElicitComplete`][crate::ElicitComplete]: fully
1075///   introspectable, serialisable, and reasoned about by elicitation tooling.
1076/// - `Invariant` — a [`Prop`] that every valid transition must preserve.
1077///
1078/// Transitions are [`FormalMethod`]s with signature
1079/// `(State, Established<Invariant>) -> (State, Established<Invariant>)`.
1080/// The type system guarantees that a transition cannot produce a new state
1081/// without presenting proof that the invariant still holds.
1082///
1083/// # "Gated community" for formal verification
1084///
1085/// Declaring a `VerifiedStateMachine` is the top-level compiler-enforced
1086/// claim that a system preserves its invariants. Outside a VSM any piece of
1087/// the contracts stack can be used freely; inside, every transition must be
1088/// a `FormalMethod`.
1089///
1090pub trait VerifiedStateMachine {
1091    /// The state type.  Must be [`ElicitComplete`][crate::ElicitComplete].
1092    type State: crate::ElicitComplete;
1093
1094    /// The invariant that every transition must preserve.
1095    type Invariant: Prop;
1096
1097    /// Return the Kani harness [`proc_macro2::TokenStream`] for every
1098    /// transition in this machine.
1099    ///
1100    /// Override this in each `VerifiedStateMachine` implementation, listing the
1101    /// companion structs generated by [`formal_method`][crate::formal_method]:
1102    ///
1103    /// ```rust,ignore
1104    /// fn transition_harnesses() -> Vec<proc_macro2::TokenStream> {
1105    ///     vec![
1106    ///         MyTransitionATransition::kani_harness(),
1107    ///         MyTransitionBTransition::kani_harness(),
1108    ///     ]
1109    /// }
1110    /// ```
1111    ///
1112    /// The default implementation returns an empty list (no harnesses).
1113    #[cfg(not(kani))]
1114    fn transition_harnesses() -> Vec<proc_macro2::TokenStream> {
1115        vec![]
1116    }
1117
1118    /// Compose the full VSM Kani proof: the invariant proposition proof
1119    /// followed by all `proof_for_contract` closure harnesses for each
1120    /// registered transition.
1121    ///
1122    /// This is the token stream that a `build.rs` should write to a generated
1123    /// `.rs` file so that Kani can verify the entire state machine end-to-end.
1124    ///
1125    /// The composition says: "the invariant is a valid proposition AND every
1126    /// transition preserves it without panicking for any reachable input."
1127    #[cfg(not(kani))]
1128    fn vsm_kani_proof() -> proc_macro2::TokenStream {
1129        let mut ts = Self::Invariant::kani_proof();
1130        let inv_fn = Self::Invariant::kani_invariant_fn_name();
1131        for closure in Self::transition_kani_closure_proofs(inv_fn) {
1132            ts.extend(closure);
1133        }
1134        ts
1135    }
1136
1137    /// Return one `proof_for_contract` closure harness per transition in this machine.
1138    ///
1139    /// Each entry is a `#[kani::proof_for_contract(fn_name)]` harness using the
1140    /// forgive-and-forget pattern.  Contracts on the original function (emitted by
1141    /// `#[formal_method]` via `cfg_attr(kani, kani::requires/ensures)`) are verified
1142    /// by DFCC.  Once verified, each transition can be replaced with
1143    /// `stub_verified(fn_name)` in multi-step composition harnesses.
1144    ///
1145    /// When `inv_fn` is empty, all entries are empty `TokenStream`s.
1146    ///
1147    /// The default implementation returns an empty list.
1148    #[cfg(not(kani))]
1149    fn transition_kani_closure_proofs(_inv_fn: &str) -> Vec<proc_macro2::TokenStream> {
1150        vec![]
1151    }
1152
1153    /// Return one Creusot companion contract per transition in this machine.
1154    ///
1155    /// Each entry is a `#[cfg(creusot)]` function with `#[requires]`/`#[ensures]`
1156    /// annotations. When the invariant type's [`Prop::creusot_invariant_fn_name`]
1157    /// returns a non-empty string, the contracts are real (no `#[trusted]`).
1158    /// Otherwise they fall back to `#[trusted]` placeholders.
1159    ///
1160    /// The default implementation returns an empty list.
1161    #[cfg(not(kani))]
1162    fn transition_creusot_contracts(_inv_fn: &str) -> Vec<proc_macro2::TokenStream> {
1163        vec![]
1164    }
1165
1166    /// Return one Verus `assume_specification` block per transition in this machine.
1167    ///
1168    /// When `inv_fn` is non-empty, each entry constrains the transition with
1169    /// `requires inv_fn(&state)` / `ensures inv_fn(&r.0)`.  When `inv_fn` is
1170    /// empty, trivially-true `requires true, ensures true` stubs are emitted.
1171    ///
1172    /// The default implementation returns an empty list.
1173    #[cfg(not(kani))]
1174    fn transition_verus_contracts(_inv_fn: &str) -> Vec<proc_macro2::TokenStream> {
1175        vec![]
1176    }
1177
1178    /// Return one `#[verifier::external]` stub function per transition.
1179    ///
1180    /// Used by `vsm_verus_standalone_proof()` to generate self-contained Verus
1181    /// source files where no external crate rlibs are available.  Each stub
1182    /// declares the function with a `todo!()` body so that `assume_specification`
1183    /// has a concrete symbol to axiomatise.
1184    ///
1185    /// The default implementation returns an empty list.
1186    #[cfg(not(kani))]
1187    fn transition_verus_stubs() -> Vec<proc_macro2::TokenStream> {
1188        vec![]
1189    }
1190
1191    /// Compose the full VSM Creusot proof: the invariant proposition proof
1192    /// followed by one contract per registered transition.
1193    ///
1194    /// When `Self::Invariant::creusot_invariant_fn_name()` is non-empty, the
1195    /// generated companions use real `#[requires]`/`#[ensures]` annotations and
1196    /// Creusot will verify the function bodies. Otherwise trusted placeholders
1197    /// are emitted (same as the stub path).
1198    #[cfg(not(kani))]
1199    fn vsm_creusot_proof() -> proc_macro2::TokenStream {
1200        let mut ts = Self::Invariant::creusot_proof();
1201        let inv_fn = Self::Invariant::creusot_invariant_fn_name();
1202        for contract in Self::transition_creusot_contracts(inv_fn) {
1203            ts.extend(contract);
1204        }
1205        ts
1206    }
1207
1208    /// Compose the full VSM Verus proof: the invariant proposition proof
1209    /// followed by one `assume_specification` block per registered transition.
1210    ///
1211    /// When `Self::Invariant::verus_invariant_fn_name()` is non-empty, the
1212    /// generated companions emit real `requires`/`ensures` Verus contracts.
1213    /// Otherwise trivially-true stubs are emitted.
1214    #[cfg(not(kani))]
1215    fn vsm_verus_proof() -> proc_macro2::TokenStream {
1216        let mut ts = Self::Invariant::verus_proof();
1217        let inv_fn = Self::Invariant::verus_invariant_fn_name();
1218        for contract in Self::transition_verus_contracts(inv_fn) {
1219            ts.extend(contract);
1220        }
1221        ts
1222    }
1223
1224    /// Compose the transition portion of a self-contained Verus proof file:
1225    /// all `#[verifier::external]` stubs followed by all `assume_specification`
1226    /// contracts.
1227    ///
1228    /// Used by `strictly_verus/build.rs` to build files that do **not** import
1229    /// external crate rlibs (which the Verus toolchain cannot link).  The
1230    /// caller is responsible for prepending the inline type definitions and
1231    /// the invariant spec function before this output.
1232    #[cfg(not(kani))]
1233    fn vsm_verus_transitions() -> proc_macro2::TokenStream {
1234        let mut ts = proc_macro2::TokenStream::new();
1235        for stub in Self::transition_verus_stubs() {
1236            ts.extend(stub);
1237        }
1238        let inv_fn = Self::Invariant::verus_invariant_fn_name();
1239        for contract in Self::transition_verus_contracts(inv_fn) {
1240            ts.extend(contract);
1241        }
1242        ts
1243    }
1244}
1245
1246/// Convenience alias: a verified transition for state machine `VSM`.
1247///
1248/// Any function (or closure) whose signature is
1249/// `(VSM::State, Established<VSM::Invariant>) -> (VSM::State, Established<VSM::Invariant>)`
1250/// satisfies this bound automatically via the [`FormalMethod`] blanket impl.
1251pub trait VerifiedTransition<VSM: VerifiedStateMachine>:
1252    FormalMethod<VSM::State, VSM::Invariant, VSM::State, VSM::Invariant>
1253{
1254}
1255
1256/// Blanket impl: any `FormalMethod` over the right state/invariant types is a
1257/// `VerifiedTransition` for the corresponding `VerifiedStateMachine`.
1258impl<VSM, T> VerifiedTransition<VSM> for T
1259where
1260    VSM: VerifiedStateMachine,
1261    T: FormalMethod<VSM::State, VSM::Invariant, VSM::State, VSM::Invariant>,
1262{
1263}
1264
1265/// Per-variant concrete construction expressions at each compositional depth.
1266///
1267/// Used by `#[derive(VerifiedStateMachine)]` to emit three harnesses per
1268/// `(transition × variant)`: one per depth.  Each depth uses `KaniCompose`
1269/// field expressions from `#[derive(KaniVariantState)]`.
1270pub struct KaniVariantConstruction {
1271    /// Snake_case variant name suffix for harness function names
1272    /// (e.g. `"explain_view"` for `ExplainView`).
1273    pub variant_name: &'static str,
1274    /// Depth-0 construction expression: all collections empty / `None`.
1275    pub depth0: String,
1276    /// Depth-1 construction expression: one element in each collection.
1277    pub depth1: String,
1278    /// Depth-2 construction expression: two elements in each collection.
1279    pub depth2: String,
1280}
1281
1282/// Per-variant concrete construction expressions for VSM state enums.
1283///
1284/// Implemented via `#[derive(KaniVariantState)]`. Used by `derive_vsm` to
1285/// generate per-variant Kani harnesses — three harnesses per
1286/// `(transition × variant)`, one per compositional depth — so that CBMC
1287/// receives a concrete discriminant and bounded fields instead of a fully
1288/// symbolic enum.
1289///
1290/// # Motivation
1291///
1292/// `kani::any::<StateEnum>()` creates a tagged union where ALL variant fields
1293/// are globally symbolic in CBMC. Dropping such a value requires reasoning
1294/// about every variant destructor simultaneously, causing unbounded unwinding
1295/// for variants that contain `Vec<T>` or `String` (non-trivial destructors).
1296///
1297/// Per-variant harnesses give CBMC a concrete discriminant for each proof,
1298/// eliminating the symbolic-enum-drop problem while preserving exhaustive
1299/// coverage through case analysis.  Per-depth harnesses extend this to
1300/// cover recursive / collection fields at sizes 0, 1, and 2.
1301pub trait KaniVariantState {
1302    /// Returns per-variant construction expressions at all three depths.
1303    ///
1304    /// Each [`KaniVariantConstruction`] provides:
1305    ///
1306    /// - `variant_name` — snake_case suffix for the harness function name.
1307    /// - `depth0` — all collections empty / `None` (base case).
1308    /// - `depth1` — one element in each collection (inductive step).
1309    /// - `depth2` — two elements in each collection (inductive step ×2).
1310    ///
1311    /// Field expressions use `<T as KaniCompose>::kani_depth{n}()` for
1312    /// non-primitive types, `Vec::new()` / `None` / `String::new()` for
1313    /// recognized collection types, and `kani::any()` for primitives.
1314    fn kani_variant_constructions() -> Vec<KaniVariantConstruction>;
1315}
1316
1317// ── Kani-safe label helpers ───────────────────────────────────────────────────
1318
1319/// Produce a label string for display / diagnostics in formal-method transitions.
1320///
1321/// In normal builds, expands to `format!($args*)` as written.  In Kani builds,
1322/// returns `String::new()` instead so CBMC does not model the entire Rust
1323/// formatter machinery — `format!()` involves trait-object dispatch through
1324/// `std::fmt` that multiplies CBMC paths even for fully concrete inputs.
1325///
1326/// # Usage
1327///
1328/// Replace every `format!("...")` that produces a *display label* (content
1329/// irrelevant to invariant correctness) in a `#[formal_method]` transition
1330/// body with `kani_label!("...")`:
1331///
1332/// ```rust,ignore
1333/// let label_left = kani_label!("{old_schema}.{old_table}");
1334/// ```
1335#[macro_export]
1336macro_rules! kani_label {
1337    ($($arg:tt)*) => {{
1338        #[cfg(any(kani, creusot))]
1339        let _s = ::std::string::String::new();
1340        #[cfg(not(any(kani, creusot)))]
1341        let _s = format!($($arg)*);
1342        _s
1343    }};
1344}