Implement a new invariant for jolt-eval
This skill handles all the boilerplate: creating the invariant struct + input type, implementing the Invariant trait, registering it in the JoltInvariants enum, creating a fuzz target (if applicable), and running sync_targets.sh.
</Purpose>
<Execution_Policy>
sumcheck_binding).{{ARGUMENTS}}: must be a valid Rust identifier (lowercase alphanumeric + underscores). Reject otherwise.description()Test, Fuzz, RedTeam)Setup = ())jolt-eval/src/invariant/mod.rs to understand the current JoltInvariants enum and dispatch! macro.jolt-eval/src/invariant/split_eq_bind.rsjolt-eval/src/invariant/soundness.rsCreate the invariant file at jolt-eval/src/invariant/<invariant_name>.rs with:
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
pub struct <Name>Input {
// fields
}
impl<'a> Arbitrary<'a> for <Name>Input {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
// Generate random inputs with reasonable bounds
}
}
Key requirements for the input type:
Debug, Clone, Serialize, Deserialize, JsonSchemaArbitrary manually (for fuzzing)Arbitrary impl (e.g. u.int_in_range(2..=16)?) to keep inputs meaningful#[jolt_eval_macros::invariant(Test, Fuzz, RedTeam)] // adjust targets as needed
#[derive(Default)]
pub struct <Name>Invariant;
impl Invariant for <Name>Invariant {
type Setup = (); // or a custom setup type
type Input = <Name>Input;
fn name(&self) -> &str { "<invariant_name>" }
fn description(&self) -> String { "...".into() }
fn setup(&self) -> Self::Setup { /* ... */ }
fn check(&self, setup: &Self::Setup, input: Self::Input) -> Result<(), CheckError> {
// 1. Validate input — return Err(CheckError::InvalidInput(...)) for degenerate cases
// 2. Run the property check
// 3. Return Ok(()) if the invariant holds
// 4. Return Err(CheckError::Violation(...)) if violated
}
fn seed_corpus(&self) -> Vec<Self::Input> {
// Include: minimal case, typical case, edge case (large values, boundary conditions)
}
}
check()CheckError::InvalidInput for degenerate inputs that should be skipped (not counted as violations)CheckError::Violation(InvariantViolation::with_details(...)) for actual violations — include diagnostic infoEdit jolt-eval/src/invariant/mod.rs:
pub mod <invariant_name>; to the module declarations at the top.JoltInvariants:
<VariantName>(<invariant_name>::<Name>Invariant),
JoltInvariants::all():
Self::<VariantName>(<invariant_name>::<Name>Invariant),
Use <Name>Invariant::default() if the struct has fields.dispatch! macro:
JoltInvariants::<VariantName>($inv) => $body,
Create jolt-eval/fuzz/fuzz_targets/<invariant_name>.rs:
#![no_main]
use jolt_eval::invariant::<invariant_name>::<Name>Invariant;
jolt_eval::fuzz_invariant!(<Name>Invariant::default());
Then run ./jolt-eval/sync_targets.sh to update fuzz/Cargo.toml.
Run these commands (all must pass):
# Format
cargo fmt -q
# Lint
cargo clippy -p jolt-eval -q --all-targets -- -D warnings
# Run auto-generated tests (seed_corpus + random_inputs)
cargo nextest run -p jolt-eval --cargo-quiet invariant::<invariant_name>
# If fuzz target was created, verify it compiles
cd jolt-eval/fuzz && cargo check 2>&1 | head -20
If any step fails, fix the issue and re-run.
Task: Implement a new invariant for jolt-eval. {{ARGUMENTS}}