stateright 0.25.0

A model checker for implementing distributed systems.
Documentation
# Changes

## 0.25.0

I was recently asked where linearizability was specified in the actor examples.
The answer was `RegisterCfg::into_model`, but this question provided useful
feedback: the existing structure was hiding information important for
understanding each model.

This release addresses that gap by removing `RegisterCfg`. Instead the actor
examples fully specify their `ActorModel`s. This requirement is made less
verbose by introducing two helpers, `RegisterMsg::record_invocations` and
`RegisterMsg::record_returns`.

Here is a representative example prior to this release:

```rust
RegisterCfg {
        client_count: 2,
		servers: vec![
			PaxosActor { peer_ids: model_peers(0, 3) },
			PaxosActor { peer_ids: model_peers(1, 3) },
			PaxosActor { peer_ids: model_peers(2, 3) },
		],
    }
    .into_model()
    .duplicating_network(DuplicatingNetwork::No)
    .within_boundary(within_boundary)
    .checker()
```

As mentioned above, `RegisterCfg::into_model` was implemented inside the
library, thereby inadvertantly hiding important details about the resulting
model. Furthermore, `within_boundary` lacked access to sufficient
configuration:

```rust
fn within_boundary<H>(
    _: &RegisterCfg<PaxosActor>,
    state: &ActorModelState<RegisterActor<PaxosActor>, H>)
    -> bool
{
    state.actor_states.iter().all(|s| {
        if let RegisterActorState::Server(s) = &**s {
            s.ballot.0 <= 3
        } else {
            true
        }
    })
}
```

The new pattern is to introduce a model-specific configuration type, which has
the added benefit of enabling additional model-specific parameters:

```rust
PaxosModelCfg {
        client_count: 2,
        server_count: 3,
        max_round: 3,
    }
    .into_model().checker()
```

The same program would then specify a dedicated `into_model`, thereby
addressing the motivating question of this release. For example:

```rust
impl PaxosModelCfg {
    fn into_model(self) ->
        ActorModel<
            RegisterActor<PaxosActor>,
            Self,
            LinearizabilityTester<Id, Register<Value>>>
    {
        ActorModel::new(
                self.clone(),
                LinearizabilityTester::new(Register(Value::default()))
            )
            .actors((0..self.server_count)
                    .map(|i| RegisterActor::Server(PaxosActor {
                        peer_ids: model_peers(i, self.server_count),
                    })))
            .actors((0..self.client_count)
                    .map(|_| RegisterActor::Client {
                        server_count: self.server_count,
                    }))
            .duplicating_network(DuplicatingNetwork::No)
            .property(Expectation::Always, "linearizable", |_, state| {
                state.history.serialized_history().is_some()
            })
            .property(Expectation::Sometimes, "value chosen", |_, state| {
                for env in &state.network {
                    if let RegisterMsg::GetOk(_req_id, value) = env.msg {
                        if value != Value::default() { return true; }
                    }
                }
                false
            })
            .record_msg_in(RegisterMsg::record_returns)
            .record_msg_out(RegisterMsg::record_invocations)
            .within_boundary(|cfg, state| {
                state.actor_states.iter().all(|s| {
                    if let RegisterActorState::Server(s) = &**s {
                        s.ballot.0 <= cfg.max_round
                    } else {
                        true
                    }
                })
            })
    }
}
```

The release also introduces a supporting `ConsistencyTester` trait generalizing
`LinearizabilityTester` and `SequentialConsistencyTester`. This new trait
enables helpers (such as `RegisterMsg::record_invocations`) to be used with
models expecting linearizability, sequential consistency, or other to-be-added
consistency semantics.