# `Pixir.Log`
[🔗](https://github.com/Ranvier-Technologies/pixir/blob/main/lib/pixir/log.ex#L1)

The per-Session **Log** (ADR 0003 / 0004): an append-only NDJSON file at
`.pixir/sessions/<id>.ndjson`, the single source of truth for a Session.

Only **canonical** Events are written — one `Pixir.Event` per line, serialized 1:1.
`fold/2` replays the file into **History**: the ordered list of canonical Events
(by `seq`, with file append order as the backstop, per ADR 0004).

Append is the deliberate exception to the temp+rename rule (the file only ever grows
and a single Session process serializes writes). When a Session writer lease exists,
appends must present the matching holder; raw append remains available only for cold
fixture/import paths where no writer lease is active. Ephemeral Events are never logged.

Public Log operations validate the Session id and `lstat` the Pixir-owned path below
the trusted Workspace root before use. Existing and dangling symlinks are refused.
The check is a deterministic preflight, not protection against a same-UID process
replacing a component between check and use.

# `history`

```elixir
@type history() :: [Pixir.Event.t()]
```

# `append`

```elixir
@spec append(
  Pixir.Event.t(),
  keyword()
) :: {:ok, Pixir.Event.t()} | {:error, map()}
```

Append a **canonical** Event to the Session's Log. Ephemeral Events are rejected
with a structured error (ADR 0005) — logging one is a programming error.

Returns `{:ok, event}` on success.

# `create_session`

```elixir
@spec create_session(String.t(), [Pixir.Event.t()], keyword()) ::
  {:ok, [Pixir.Event.t()]} | {:error, map()}
```

Create a new Session Log from canonical Events in one atomic write (temp + rename).

Unlike `append/2`, this refuses when the Log already exists and writes the full file at
once. Used for fork child Log creation where partial NDJSON must not remain on failure.

# `exists`

```elixir
@spec exists(
  String.t(),
  keyword()
) :: {:ok, boolean()} | {:error, map()}
```

Return structured Session Log existence.

Unlike `exists?/2`, this preserves invalid-id and unsafe-state-path errors so public
callers do not collapse confinement failures into a misleading `not_found` result.

# `exists?`

```elixir
@spec exists?(
  String.t(),
  keyword()
) :: boolean()
```

Whether a Log file exists for this Session yet (compatibility boolean).

# `fold`

```elixir
@spec fold(
  String.t(),
  keyword()
) :: {:ok, history()} | {:error, map()}
```

Fold the Log into **History** — the ordered list of canonical Events. A missing Log
is an empty History. Returns `{:ok, history}` or a structured error if a line is
unparseable.

# `fold_append_order`

```elixir
@spec fold_append_order(
  String.t(),
  keyword()
) :: {:ok, history()} | {:error, map()}
```

Fold the Log in physical append order.

This is a narrow evidence accessor for checks whose trust boundary must not depend on
caller-authored `seq` values. Normal History consumers should keep using `fold/2`,
which preserves the canonical seq-ordered replay contract.

# `path`

```elixir
@spec path(
  String.t(),
  keyword()
) :: String.t()
```

Absolute path to a Session's Log. Accepts `:workspace` (default: cwd).

---

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