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
@type opt() :: {:initial_configuration, Enumerable.t()} | {:selection, selection()} | {:deep_link, String.t() | StatifierUI.Trace.DeepLink.t() | nil}
Options shared by the fold functions.
@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.
@type selection() :: :live | {:macrostep, non_neg_integer()}
Which point in the run the panes show: the live tip, or one macrostep.
Functions
@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.
@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.
@spec diagram(Statifier.Machine.t(), [StatifierUI.Trace.Message.t()], [opt()]) :: String.t()
Mermaid stateDiagram-v2 source for the configuration pane:
StatifierUI.Diagram.render/2 over active_configuration/2.
@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.
@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.
@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 [].
@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).
@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.
@spec status(StatifierUI.Trace.Subscriber.stats()) :: String.t()
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.
:livereturns:livefrom anywhere.:firstselects the oldest macrostep in view.:prevfrom:livepins 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.:nextselects the oldest macrostep above the current one, and returns to:liveonce 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.