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.
Summary
Functions
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.
Create a new Session Log from canonical Events in one atomic write (temp + rename).
Return structured Session Log existence.
Whether a Log file exists for this Session yet (compatibility boolean).
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 the Log in physical append order.
Select bounded History from a Session Log without scanning omitted bytes.
Absolute path to a Session's Log. Accepts :workspace (default: cwd).
Types
@type history() :: [Pixir.Event.t()]
Functions
@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.
@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.
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.
Whether a Log file exists for this Session yet (compatibility boolean).
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 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.
@spec fold_bounded( String.t(), keyword() ) :: {:ok, %{history: history(), selection: map()}} | {:error, map()}
Select bounded History from a Session Log without scanning omitted bytes.
Accepts :workspace, :max_log_bytes (default 8 MiB), and optional
:max_events (a nonnegative integer; absent means no event cap). Logs exceeding
either bound retain complete prefix/tail records using at most two positional
reads. Event selection uses original record byte spans, not reencoded Events.
Caps below two permit complete under-limit History but refuse two-ended sampling.
The returned
%{history: events, selection: metadata} reports exact omitted bytes, but omitted
event counts remain unknown. A complete JSON record at EOF needs no final LF;
unfinished append bytes are explicitly reported, never repaired or persisted.
Unlike fold/2, a missing Log returns :log_not_found. Selected records must have
a nonnegative integer seq, the requested Session identity, and map data. Complete
selections follow normal seq order; partial selections must already be strictly
ordered. Duplicate selected seqs fail. These stricter selection checks do not
change ordinary fold or append-order replay compatibility.
Absolute path to a Session's Log. Accepts :workspace (default: cwd).