StatifierUI.Inspector (StatifierUI v0.3.0)

Copy Markdown View Source

Pure pane assembly for the Livebook inspector: folds a compiled Statifier.Machine and a StatifierUI.Trace.Subscriber message list into the render source each pane displays - Mermaid source for the configuration diagram, Markdown for the event log and the datamodel explorer, and a status line from the subscriber's stats/1 snapshot.

No Kino, no process, no session: everything here is testable from a message list, exactly like the pane modules it composes. The Kino shell (StatifierUI.Kino) maps these strings into widgets and owns nothing else.

The active configuration is read from the newest message that stamped one - a trace.macrostep_stable, the quiescent configuration of the last completed macrostep, or a trace.done, the configuration a halted run exited in (docs/wire-format.md). Before either has arrived in view, the caller-supplied initial configuration (typically Statifier.Session.snapshot/1's) is used instead.

A halted chart's final configuration (sui-dc7)

A run that ends by entering a top-level <final> never stabilizes in its last macrostep: quiescence is never reached, so no trace.macrostep_stable is emitted for it and the engine stamps the configuration on trace.done instead. Reading only macrostep_stable therefore left the diagram highlighting the state the chart left, while the datamodel pane showed the assignment that moved it out.

Both stamps are read, and the wire format is what settles that they may be: its trace.done row defines configuration as "the full configuration as it stood at exit, a genuine set, sorted ascending" - the same field, shape, and authority as a macrostep_stable payload. Nothing here re-derives an exit configuration from the exit sets that precede trace.done; that would be re-implementing Appendix D, which this repo does not do (ADR-0002, StatifierUI.EventLog). The two readings stay labelled apart, so a configuration the chart exited in is never presented as one it settled in.

Selecting a past macrostep (sui-3gg)

Every fold function takes a :selection, which is :live (the default, the behaviour above) or {:macrostep, n}. Under a selection the diagram shows the configuration macrostep n settled in, the event log marks that macrostep's entry - shown in the diagram and opens it, and selection_note/2 renders the line that says which point is on screen. That is the whole of the link between the two panes.

Nothing here re-derives a configuration. Every configuration this module can show was stamped by the engine on a trace.macrostep_stable or a trace.done, and a stream reconstructed by catch-up got there through statifier ADR-0034 replay, which re-drives the core in a pure fold rather than rewinding a live session. Time travel is a read of replay output as data (ADR-0002's inherited clause), so selecting a macrostep neither touches the session nor asks the engine for anything new.

A selected macrostep that stamped no configuration of its own - in flight, or its stamp dropped - has none to draw. Rather than draw nothing, the newest configuration at or below it is shown and selection_note/2 says which macrostep it was carried from - a carried configuration is never presented as a measured one.

Linking out to a trace (sui-4w2)

Every fold function also takes :deep_link, the host's APM URL template (ADR-0013). With one configured, the event log's macrostep summaries and selection_note/2 gain an [open trace](...) link for each macrostep whose messages carry the wire format's otel key. Without the option, or on a macrostep carrying no correlation, every pane renders exactly as it did - including the carried-forward wording above, which the link is appended to rather than replacing.

Summary

Types

Options shared by the fold functions.

One selectable point, for a caller building a scrubber.

Which point in the run the panes show: the live tip, or one macrostep.

Functions

The configuration messages and opts[:selection] imply.

Markdown for the datamodel explorer pane: StatifierUI.DatamodelExplorer.build_live/1 over messages, rendered with the default markers. A build failure renders as a visible error line, same policy as event_log/1.

Mermaid stateDiagram-v2 source for the configuration pane: StatifierUI.Diagram.render/2 over active_configuration/2.

Markdown for the event log pane: StatifierUI.EventLog.build/1 rendered collapsible. The selected macrostep is opened and marked - shown in the diagram; with no selection the last macrostep is opened, as before. A build failure renders as a visible error line rather than raising - the inspector keeps showing the other panes.

The status header for a stream read back from storage rather than watched live.

The macrosteps a caller can select, oldest first - the scrubber's own vocabulary, so a UI never has to fold the log itself. A log that refuses to build yields [].

How the selection resolved, for a caller that needs to say so: :live, {:quiescent, n}, {:final, n} (macrostep n halted the run, so the configuration it exited in is shown), {:carried, n, from} (macrostep n stamped no configuration, so macrostep from's is shown), or {:before_first, n} (nothing at or below n stamped a configuration, so the initial configuration is shown).

The one-line note naming the point on screen, for display above the diagram: which macrostep and its event, whether the configuration was measured there or carried from an earlier macrostep, and - on :live - that the diagram is following the tip.

The one-line (plus warnings) status header: session id, subscriber status, message and drop counts, and one blockquote line per diagnostic. A :not_recorded or :catch_up_failed diagnostic is what labels the whole inspector live-only - a partial stream is never presented as whole (statifier ADR-0049; this bead's precondition note).

Moves a selection one scrubber step over points, purely - the widget layer holds the selection, this decides where a move lands.

Types

opt()

@type opt() ::
  {:initial_configuration, Enumerable.t()}
  | {:selection, selection()}
  | {:deep_link, String.t() | StatifierUI.Trace.DeepLink.t() | nil}

Options shared by the fold functions.

point()

@type point() :: %{
  macrostep: non_neg_integer(),
  event: String.t() | nil,
  quiescent?: boolean(),
  final?: boolean()
}

One selectable point, for a caller building a scrubber.

quiescent? says the macrostep settled, final? that it halted the run (sui-dc7). They are never both true, and a macrostep with a configuration to draw is one where either is - which is why final? was added beside quiescent? rather than widening it: a halting macrostep is not quiescent, and a scrubber saying so is not the same as one saying it has nothing to show.

selection()

@type selection() :: :live | {:macrostep, non_neg_integer()}

Which point in the run the panes show: the live tip, or one macrostep.

Functions

active_configuration(messages, opts \\ [])

@spec active_configuration([StatifierUI.Trace.Message.t()], [opt()]) :: [
  non_neg_integer()
]

The configuration messages and opts[:selection] imply.

On :live (the default): the configuration payload of the newest trace.macrostep_stable or trace.done, whichever arrived last, or opts[:initial_configuration] (default []) when neither is in view.

On {:macrostep, n}: the configuration macrostep n settled in - or, for the macrostep that halted the run, exited in - per StatifierUI.EventLog.configuration_at/2, falling back to the newest one below it when n stamped neither, and to opts[:initial_configuration] when nothing at or below n stamped one. A message list the log refuses (mixed sessions) degrades to the live reading rather than raising, same policy as event_log/2.

datamodel(messages)

@spec datamodel([StatifierUI.Trace.Message.t()]) :: String.t()

Markdown for the datamodel explorer pane: StatifierUI.DatamodelExplorer.build_live/1 over messages, rendered with the default markers. A build failure renders as a visible error line, same policy as event_log/1.

diagram(machine, messages, opts \\ [])

Mermaid stateDiagram-v2 source for the configuration pane: StatifierUI.Diagram.render/2 over active_configuration/2.

event_log(messages, opts \\ [])

@spec event_log([StatifierUI.Trace.Message.t()], [opt()]) :: String.t()

Markdown for the event log pane: StatifierUI.EventLog.build/1 rendered collapsible. The selected macrostep is opened and marked - shown in the diagram; with no selection the last macrostep is opened, as before. A build failure renders as a visible error line rather than raising - the inspector keeps showing the other panes.

persisted_status(messages)

@spec persisted_status([StatifierUI.Trace.Message.t()]) :: String.t()

The status header for a stream read back from storage rather than watched live.

A persisted stream has no StatifierUI.Trace.Subscriber behind it, so there is no status, no buffered or dropped count, and no diagnostics - and this says persisted rather than inventing them. StatifierUI.Live's status pane takes the same position for the same reason (status_kind(nil)), so both surfaces describe a reloaded trace the same way.

What a persisted stream can still say is whether it is projected: the projection header rides on its own session.start message (ADR-0012), so a reloaded capture that withholds values still announces that it does.

points(messages)

@spec points([StatifierUI.Trace.Message.t()]) :: [point()]

The macrosteps a caller can select, oldest first - the scrubber's own vocabulary, so a UI never has to fold the log itself. A log that refuses to build yields [].

resolution(messages, opts \\ [])

@spec resolution([StatifierUI.Trace.Message.t()], [opt()]) ::
  :live
  | {:quiescent, non_neg_integer()}
  | {:final, non_neg_integer()}
  | {:carried, non_neg_integer(), non_neg_integer()}
  | {:before_first, non_neg_integer()}

How the selection resolved, for a caller that needs to say so: :live, {:quiescent, n}, {:final, n} (macrostep n halted the run, so the configuration it exited in is shown), {:carried, n, from} (macrostep n stamped no configuration, so macrostep from's is shown), or {:before_first, n} (nothing at or below n stamped a configuration, so the initial configuration is shown).

selection_note(messages, opts \\ [])

@spec selection_note([StatifierUI.Trace.Message.t()], [opt()]) :: String.t()

The one-line note naming the point on screen, for display above the diagram: which macrostep and its event, whether the configuration was measured there or carried from an earlier macrostep, and - on :live - that the diagram is following the tip.

status(stats)

The one-line (plus warnings) status header: session id, subscriber status, message and drop counts, and one blockquote line per diagnostic. A :not_recorded or :catch_up_failed diagnostic is what labels the whole inspector live-only - a partial stream is never presented as whole (statifier ADR-0049; this bead's precondition note).

step(selection, points, move)

@spec step(selection(), [point()], :live | :first | :prev | :next) :: selection()

Moves a selection one scrubber step over points, purely - the widget layer holds the selection, this decides where a move lands.

  • :live returns :live from anywhere.
  • :first selects the oldest macrostep in view.
  • :prev from :live pins the newest macrostep (the same picture, no longer following the tip); from a macrostep it selects the newest one below it, staying put at the oldest.
  • :next selects the oldest macrostep above the current one, and returns to :live once there is none - so a run driven forward while the scrubber sits at the tip keeps following it.

With no points at all every move returns :live: there is nothing else to show.