- name
- new-invariant
- description
- Implement a new invariant for jolt-eval
- argument-hint
- <invariant-name>
<Purpose>
Implement a new invariant for the jolt-eval framework. An invariant is a property that must always hold — the framework can test it with seed inputs, fuzz it with random/structured inputs, and red-team it with an AI agent.
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>
- The user must provide an invariant name (lowercase with underscores, e.g. `sumcheck_binding`).
- Ask the user what property is being checked and what the input type should look like before writing code.
- Follow existing patterns exactly — study the split_eq_bind and soundness invariants as models.
- Always run clippy and the auto-generated tests before reporting success.
</Execution_Policy>
<Steps>
## Phase 1: Gather Requirements
1. Validate the argument `{{ARGUMENTS}}`: must be a valid Rust identifier (lowercase alphanumeric + underscores). Reject otherwise.
2. Ask the user:
- What property does this invariant check? (becomes the `description()`)
- What does the input look like? (fields, types, ranges)
- What synthesis targets should it support? (`Test`, `Fuzz`, `RedTeam`)
- Does it need non-trivial setup? (e.g. preprocessing, compilation — default to `Setup = ()`)
## Phase 2: Explore Context
1. Read `jolt-eval/src/invariant/mod.rs` to understand the current `JoltInvariants` enum and `dispatch!` macro.
2. Read an existing invariant for reference:
- Simple: `jolt-eval/src/invariant/split_eq_bind.rs`
- Complex (with setup, enrich_input): `jolt-eval/src/invariant/soundness.rs`
3. If the invariant tests jolt-prover-legacy functionality, explore the relevant jolt-prover-legacy modules to understand the types and APIs involved.
## Phase 3: Implement
Create the invariant file at `jolt-eval/src/invariant/<invariant_name>.rs` with:
### Input Type
```rust
#[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:
- Must derive `Debug`, `Clone`, `Serialize`, `Deserialize`, `JsonSchema`
- Must implement `Arbitrary` manually (for fuzzing)
- Use bounded ranges in `Arbitrary` impl (e.g. `u.int_in_range(2..=16)?`) to keep inputs meaningful
### Invariant Struct
```rust
#[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)
}
}
```
### Guidelines for `check()`
- Use `CheckError::InvalidInput` for degenerate inputs that should be skipped (not counted as violations)
- Use `CheckError::Violation(InvariantViolation::with_details(...))` for actual violations — include diagnostic info
- Compare against a known-correct reference implementation when testing optimized code
## Phase 4: Register
Edit `jolt-eval/src/invariant/mod.rs`:
1. Add `pub mod <invariant_name>;` to the module declarations at the top.
2. Add a variant to `JoltInvariants`:
```rust
<VariantName>(<invariant_name>::<Name>Invariant),
```
3. Add the variant to `JoltInvariants::all()`:
```rust
Self::<VariantName>(<invariant_name>::<Name>Invariant),
```
Use `<Name>Invariant::default()` if the struct has fields.
4. Add the variant to the `dispatch!` macro:
```rust
JoltInvariants::<VariantName>($inv) => $body,
```
## Phase 5: Create Fuzz Target (if targets include Fuzz)
Create `jolt-eval/fuzz/fuzz_targets/<invariant_name>.rs`:
```rust
#![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`.
## Phase 6: Validate
Run these commands (all must pass):
```bash
# 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.
</Steps>
Task: Implement a new invariant for jolt-eval. {{ARGUMENTS}}
Auf GitHub ansehen