# `Lockstep.History`
[🔗](https://github.com/b-erdem/lockstep/blob/v0.1.0/lib/lockstep/history.ex#L1)

Jepsen-style operation history recorder.

A history is a sequence of `:invoke` events (operations starting)
paired with `:ok` / `:fail` / `:info` completion events. It's the
raw input for consistency checkers like `Lockstep.Checker.Linearizable`.

## Recording

Each operation has three lifecycle events:

  * `invoke` — a process announces it's starting an operation
  * `ok` — the operation completed and the caller knows the result
  * `fail` — the operation explicitly didn't apply (returned an
    error the caller can interpret as "no change")
  * `info` — the operation's outcome is unknown (caller crashed,
    timed out, lost the connection mid-call). Linearizability
    checkers must consider that the op MIGHT or MIGHT NOT have
    applied.

## Quick example

    history = Lockstep.History.start_link!()

    # Concurrent workers issuing reads/writes
    for i <- 1..3 do
      Lockstep.spawn(fn ->
        Lockstep.History.op(history, :write, i, fn ->
          Register.put(reg, i)
        end)
      end)
    end

    # ... wait for them ...

    events = Lockstep.History.events(history)

    {:ok, _info} =
      Lockstep.Checker.Linearizable.check(events, Lockstep.Model.Register)

## Order

Events are indexed in the order they reach the recorder. Under
Lockstep's deterministic controller this is also the real-time
order — every `invoke`/`ok` call is a sync point routed through
the controller, so the strategy controls when each event is
recorded relative to others.

# `events`

```elixir
@spec events(pid()) :: [Lockstep.History.Event.t()]
```

Return events in invocation order (oldest first).

# `fail`

```elixir
@spec fail(pid(), atom(), any()) :: :ok
```

Record a `:fail` (explicit non-apply) completion for `f`.

# `info`

```elixir
@spec info(pid(), atom(), any()) :: :ok
```

Record an `:info` (unknown outcome) completion for `f`. Use for
crashes, timeouts, or anything where the caller can't be sure
whether the op applied.

# `init`

# `invoke`

```elixir
@spec invoke(pid(), atom(), any()) :: :ok
```

Record an `:invoke` event for `f` with input `value` from `self()`.

# `ok`

```elixir
@spec ok(pid(), atom(), any()) :: :ok
```

Record an `:ok` (success) completion for `f` with output `value`.

# `op`

```elixir
@spec op(pid(), atom(), any(), (-&gt; result)) :: result when result: var
```

Convenience wrapper: record `:invoke` before running `fun`, then
record `:ok` with the result on normal return, `:info` on raise/
exit/throw. Returns the function's result (or re-raises).

Use this when the operation has clear "succeeded with this value" or
"crashed, who knows" outcomes. For explicit "didn't apply" failures,
call `invoke/3` + `fail/3` manually.

    Lockstep.History.op(history, :write, 42, fn ->
      Register.put(reg, 42)
      :ok
    end)

# `start_link!`

```elixir
@spec start_link!() :: pid()
```

Start a history recorder under the current Lockstep iteration.
Returns the pid; pass it into worker closures so they can record.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
