tla-connect 0.0.4

TLA+/Apalache integration for model-based testing
Documentation
# tla-connect

TLA+/Apalache integration for model-based testing in Rust.

## Overview

`tla-connect` provides tools for integrating [TLA+](https://lamport.azurewebsites.net/tla/tla.html) and [Apalache](https://apalache.informal.systems/) model checking into Rust test suites. It enables:

- **Trace validation**: Verify that your implementation matches TLA+ specifications
- **Model-based testing**: Generate test cases from TLA+ models
- **Counterexample replay**: Automatically reproduce bugs found by model checkers

## Architecture

```mermaid
flowchart TB
    subgraph TLA["TLA+ Specification"]
        Spec[MySpec.tla]
        TraceSpec[TraceSpec.tla]
    end

    subgraph Apalache["Apalache Model Checker"]
        CLI[apalache-mc CLI]
        RPC[JSON-RPC Server]
    end

    subgraph Rust["Rust Implementation"]
        Driver[Driver trait]
        State[State trait]
        Emitter[StateEmitter]
    end

    subgraph Traces["Trace Formats"]
        ITF[ITF Traces]
        NDJSON[NDJSON Trace]
    end

    %% Approach 1: Batch Replay
    Spec -->|"generate_traces()"| CLI
    CLI -->|produces| ITF
    ITF -->|"replay_traces()"| Driver
    Driver -->|compared via| State

    %% Approach 2: Interactive RPC  
    Spec -->|"interactive_test()"| RPC
    RPC <-->|step-by-step| Driver

    %% Approach 3: Post-hoc Validation
    Driver -->|records| Emitter
    Emitter -->|writes| NDJSON
    NDJSON -->|"validate_trace()"| CLI
    CLI -->|checks against| TraceSpec

    classDef tla fill:#e1f5fe,stroke:#01579b
    classDef apalache fill:#fff3e0,stroke:#e65100
    classDef rust fill:#e8f5e9,stroke:#2e7d32
    classDef trace fill:#fce4ec,stroke:#880e4f

    class Spec,TraceSpec tla
    class CLI,RPC apalache
    class Driver,State,Emitter rust
    class ITF,NDJSON trace
```

| Approach | Direction | Catches |
|----------|-----------|---------|
| 1. Batch Replay | Spec → Implementation | Implementation doesn't handle a case the spec allows |
| 2. Interactive RPC | Spec ↔ Implementation | Implementation doesn't handle a case the spec allows |
| 3. Post-hoc Validation | Implementation → Spec | Implementation does something the spec doesn't allow |

## Features

- ITF (Informal Trace Format) parsing and validation
- Apalache CLI integration for trace generation
- Apalache JSON-RPC client for interactive symbolic testing
- Trace generation from TLA+ specifications
- State comparison and diff output for debugging mismatches
- Support for both file-based and RPC-based workflows

## Feature Flags

- `replay` (default): ITF trace replay against a Driver
- `trace-gen` (default): Apalache CLI trace generation
- `trace-validation` (default): Post-hoc NDJSON trace validation
- `rpc`: Interactive symbolic testing via Apalache JSON-RPC
- `parallel`: Parallel trace replay using rayon
- `full`: Enable all features

## Installation

Add this to your `Cargo.toml`:

```toml
[dependencies]
tla-connect = "0.0.2"

# For interactive RPC testing:
# tla-connect = { version = "0.0.2", features = ["rpc"] }
```

## Quick Start

### Approach 1: Batch Trace Replay

Generate traces with Apalache, then replay against your implementation:

```rust
use tla_connect::*;
use serde::Deserialize;

#[derive(Debug, PartialEq, Deserialize)]
struct MyState {
    counter: i64,
}

impl State<MyDriver> for MyState {
    fn from_driver(driver: &MyDriver) -> Result<Self, DriverError> {
        Ok(MyState { counter: driver.counter })
    }
}

struct MyDriver {
    counter: i64,
}

impl Driver for MyDriver {
    type State = MyState;

    fn step(&mut self, step: &Step) -> Result<(), DriverError> {
        switch!(step {
            "init" => { self.counter = 0; },
            "increment" => { self.counter += 1; },
            "decrement" => { self.counter -= 1; },
        })
    }
}

fn main() -> Result<(), Error> {
    let config = ApalacheConfig::builder()
        .spec("specs/Counter.tla")
        .inv("TraceComplete")
        .build();

    let generated = generate_traces(&config)?;
    replay_traces(|| MyDriver { counter: 0 }, &generated.traces)?;
    Ok(())
}
```

### Approach 2: Interactive Symbolic Testing (requires `rpc` feature)

Step-by-step symbolic execution via Apalache's explorer server:

```rust
use tla_connect::*;

#[tokio::main]
async fn main() -> Result<(), Error> {
    let client = ApalacheRpcClient::new("http://localhost:8822").await?;

    let config = InteractiveConfig::builder()
        .spec("specs/Counter.tla")
        .num_runs(100)
        .max_steps(50)
        .seed(42) // Reproducible runs
        .build();

    interactive_test(|| MyDriver::default(), &client, &config).await?;

    Ok(())
}
```

### Approach 3: Post-hoc Trace Validation

Record your implementation's execution, then validate against the spec:

```rust
use tla_connect::*;
use serde::Serialize;
use std::path::Path;

#[derive(Serialize)]
struct RecordedState {
    counter: i64,
}

fn main() -> Result<(), Error> {
    // Record execution trace
    let mut emitter = StateEmitter::new(Path::new("trace.ndjson"))?;
    emitter.emit("init", &RecordedState { counter: 0 })?;
    emitter.emit("increment", &RecordedState { counter: 1 })?;
    emitter.emit("increment", &RecordedState { counter: 2 })?;
    emitter.finish()?;

    // Validate against TLA+ spec
    let config = TraceValidatorConfig::builder()
        .trace_spec("specs/CounterTrace.tla")
        .build();

    let result = validate_trace(&config, Path::new("trace.ndjson"))?;

    match result {
        TraceResult::Valid => println!("Trace is valid!"),
        TraceResult::Invalid { reason } => println!("Invalid: {reason}"),
    }

    Ok(())
}
```

## Requirements

- Rust 1.93 or later
- Apalache (if using trace generation or validation features)

## Documentation

For detailed documentation, see [docs.rs/tla-connect](https://docs.rs/tla-connect).

## License

Licensed under either of:

- Apache License, Version 2.0 ([LICENSE-APACHE]LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT]LICENSE-MIT or http://opensource.org/licenses/MIT)

at your option.

## Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.