StatifierUI.EventLog (StatifierUI v0.1.0)

Copy Markdown View Source

Folds a [StatifierUI.Trace.Message.t()] (in any order) into a readable log keyed by (macrostep, round), per the sui-t36.5 plan.

build/1 is a pure regrouping, not an interpretation: it decides where each message goes, never what it means. Ordering comes entirely from the stamps the producer put on each message, never from arrival order or seq alone - docs/wire-format.md:91-94 names (macrostep, round) as the format's own timeline key. Feeding build/1 the same messages in a different order returns an identical struct.

Bucketing, not sorting

Buckets are ordered by (macrostep, round), and only within a bucket do messages get a further order, by {microstep, seq}. This is two levels, not one flat {macrostep, round, microstep, seq} sort key: a round is a bucket a message either belongs to or does not, so a message stamped at a lower microstep than an earlier round's messages still lands in its own round's bucket rather than sorting ahead of them. seq is itself a producer stamp (docs/wire-format.md:84-89), so using it as the final tiebreak is still ordering from the stamps.

Routing effect.* messages

The routing rule is "does this message carry a round", never a check against the "effect." prefix. When this module was written only trace.* messages and effect.budget_exhausted carried round, so the other effect.* types landed in their macrostep's effects; sui-67d then propagated round onto every effect.* type, and - exactly as planned - that was a pure data change needing no change here: an effect.* message carrying a round is routed to its round bucket, and effects still catches any message without one (an older recorded stream, for example - the must-ignore rule cuts both ways).

Errors

docs/wire-format.md:96-102 forbids merging two sessions' stamps onto one timeline (statifier ADR-0050 lets one mailbox carry an invoke tree of related sessions), so build/1 refuses a message list naming more than one session id rather than corrupting the log.

Summary

Functions

Folds messages into an t().

Types

t()

@type t() :: %StatifierUI.EventLog{
  macrosteps: [StatifierUI.EventLog.Macrostep.t()],
  session: String.t() | nil,
  session_messages: [StatifierUI.Trace.Message.t()],
  truncated?: boolean()
}

Functions

build(messages)

@spec build([StatifierUI.Trace.Message.t()]) ::
  {:ok, t()} | {:error, {:mixed_sessions, [String.t()]}}

Folds messages into an t().

Returns {:error, {:mixed_sessions, sorted_ids}} when messages names more than one distinct session. An empty list returns an empty, default log with session: nil.