From a539a8546da9cff11fd74da7d638b3048d92949a Mon Sep 17 00:00:00 2001 From: JohnnyT Date: Sat, 22 Aug 2026 18:15:58 -0600 Subject: [PATCH 1/2] Prepares the statifier_ui 0.1.0 Hex release Adds package/0 (statifier_ui, MIT, lib + mix.exs + README + LICENSE + CHANGELOG in the tarball) and the docs/0 hexdocs configuration (README landing page, architecture and wire-format guides, the ADR set), and moves the version from 0.1.0-dev to 0.1.0. The engine dependency swaps from a git pin to {:statifier, "~> 2.0"} - statifier 2.0.0 is on Hex, ending the pin arrangement (st-ADR-0066) - behind the same STATIFIER_PATH env override the sibling packages use for cross-repo co-development. Upgrading across the pin move costs one test fixture change: statifier enforces ordinal on hand-built SendDelayed and Cancel structs now (its 2.0.0 changelog names exactly this), so the normalizer test's three fixtures gain ordinal: 1. CHANGELOG.md is created with the [0.1.0] section assembled from all seven changelog.d/ fragments, deleted here per the fragment protocol; README gains a standard Hex install section. Before merge, once statifier 2.0.0 is live on Hex, mix deps.get refreshes mix.lock's statifier entry from the git pin to the Hex release and the gate re-runs without the STATIFIER_PATH override; tagging v0.1.0 and mix hex.publish stay the operator's actions after merge. Refs: sui-tx0 --- CHANGELOG.md | 69 +++++++++++++++++++++ README.md | 16 ++++- changelog.d/sui-67d.md | 8 --- changelog.d/sui-h92.md | 7 --- changelog.d/sui-t36.4.md | 7 --- changelog.d/sui-t36.5.md | 8 --- changelog.d/sui-t36.6.md | 9 --- changelog.d/sui-t36.7.md | 9 --- changelog.d/sui-t36.8.md | 16 ----- mix.exs | 57 +++++++++++++++-- test/statifier_ui/trace/normalizer_test.exs | 12 ++-- 11 files changed, 143 insertions(+), 75 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 changelog.d/sui-67d.md delete mode 100644 changelog.d/sui-h92.md delete mode 100644 changelog.d/sui-t36.4.md delete mode 100644 changelog.d/sui-t36.5.md delete mode 100644 changelog.d/sui-t36.6.md delete mode 100644 changelog.d/sui-t36.7.md delete mode 100644 changelog.d/sui-t36.8.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c66283c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,69 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +Entries for unreleased work are not written here directly. Each issue drops a +fragment in [`changelog.d/`](changelog.d/README.md); the fragments are assembled +into a version section at release. See that README for the format and for when a +change warrants an entry at all. + +## [0.1.0] 2026-08-22 + +First release: authoring, observing, and debugging components for the +[statifier](https://hex.pm/packages/statifier) statechart engine, consuming +its effect stream through the language-neutral trace wire format (format +version 1). The Livebook inspector is the first assembled frontend; the +panes underneath it are pure folds any other frontend can render. + +### Added + +- `StatifierUI.Diagram.render/2` renders a compiled machine and an active + configuration as Mermaid `stateDiagram-v2` source for `Kino.Mermaid`, with + composite nesting, parallel regions, active-state highlighting, and + cross-hierarchy transitions lifted to the composite level with a + `[lifted: ...]` marker. +- `StatifierUI.EventLog.build/1` folds a trace message stream into a log + grouped by `(macrostep, round)`, ordered by the producer's stamps rather + than arrival, and `StatifierUI.EventLog.Markdown.render/2` renders it as + collapsible Markdown for `Kino.Markdown`, with wire-format indexes + resolved to state and transition names by `StatifierUI.EventLog.Labels`. +- `StatifierUI.EventInjection.build/1` turns an ADR-0003 fixture bundle + (or `nil`) into the event-injection pane model: a sorted palette of + editable event buttons via `StatifierUI.EventInjection.Palette`, a + `free_form_only?` flag for the fixture-less degraded mode, and + `send/2`/`send_draft/3` to deliver a `StatifierUI.EventInjection.Draft` + through `Statifier.Session.send_event/2` - the ordinary recordable input + path, per statifier ADR-0029. +- `StatifierUI.DatamodelExplorer.build_authoring/3` and `build_live/2` build + a read-only datamodel tree - document `` declarations, spec + 5.10.1 system variables, predicator provider functions in scope, and + either a fixture scenario or a live session's datamodel with entries + marked `changed?` per macrostep - and + `StatifierUI.DatamodelExplorer.Markdown.render/2` renders it as Markdown + for `Kino.Markdown`. +- `StatifierUI.Kino.inspect/3` assembles the Livebook inspector: the + configuration diagram, datamodel explorer, event injection, and event + log panes composed over one shared subscriber, live-updating, detaching + cleanly on cell re-evaluation. Compiled only when the optional `:kino` + dependency is present. +- `StatifierUI.Trace.Subscriber.attach/3` accepts `catch_up: true`: on a + session started with `record: true` the missed prefix is replayed into + the buffer atomically with the subscription (statifier ADR-0049); an + unrecorded session falls back to live delivery with a `:not_recorded` + diagnostic the inspector surfaces as "Live-only". +- `StatifierUI.Inspector` - the pure pane-assembly fold the Kino shell + renders, usable by any other frontend. +- `notebooks/inspector.livemd` - the demo notebook, doubling as the + milestone's manual acceptance test. +- Serializes statifier's `DatamodelChange` effect as the + `effect.datamodel_change` wire type, so consumers can observe datamodel + values as they are written instead of only the variable names + `session.datamodel` carries. New types are additive under the wire + format's must-ignore rule. +- Every `effect.*` wire message carries the engine's `round` stamp in + its envelope, alongside `macrostep` and `microstep`; consumers reading + older recorded streams must still tolerate `effect.*` messages without + the key. diff --git a/README.md b/README.md index 46e4993..72d7235 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,22 @@ stamps them with `(macrostep, round)` counters, and retains source locations on states, transitions, and expressions. A UI is one more interpreter of those effects; the engine needs nothing changed to support it. +## Installation + +```elixir +def deps do + [ + {:statifier_ui, "~> 0.1"} + ] +end +``` + +The `:kino` (Livebook) and `:phoenix_live_view` integrations are optional +dependencies - add whichever your host actually renders with. + ## Status -Early. Nothing is published to hex yet, and the engine dependency is a git dep -until statifier publishes. The first milestone is the Livebook inspector: +Early. The first milestone is the Livebook inspector: `StatifierUI.Kino.inspect/3` over a running `Statifier.Session` - [`notebooks/inspector.livemd`](notebooks/inspector.livemd) walks it end to end. diff --git a/changelog.d/sui-67d.md b/changelog.d/sui-67d.md deleted file mode 100644 index ce3567b..0000000 --- a/changelog.d/sui-67d.md +++ /dev/null @@ -1,8 +0,0 @@ -### Added - -- Every `effect.*` wire message now carries the engine's `round` stamp in - its envelope, alongside `macrostep` and `microstep` - - `effect.budget_exhausted` already carried it, and the other nine types - gain it. An additive field under the wire format's must-ignore rule, so - the format version stays 1; consumers reading older recorded streams - must still tolerate `effect.*` messages without the key. diff --git a/changelog.d/sui-h92.md b/changelog.d/sui-h92.md deleted file mode 100644 index 97dbb48..0000000 --- a/changelog.d/sui-h92.md +++ /dev/null @@ -1,7 +0,0 @@ -### Added - -- Serializes statifier's `DatamodelChange` effect as the new - `effect.datamodel_change` wire type, so consumers can observe datamodel - values as they are written instead of only the variable names - `session.datamodel` carries. The format version stays 1; new types are - additive under the wire format's must-ignore rule. diff --git a/changelog.d/sui-t36.4.md b/changelog.d/sui-t36.4.md deleted file mode 100644 index afbca02..0000000 --- a/changelog.d/sui-t36.4.md +++ /dev/null @@ -1,7 +0,0 @@ -### Added - -- `StatifierUI.Diagram.render/2` renders a compiled machine and an active - configuration as Mermaid `stateDiagram-v2` source for `Kino.Mermaid`, with - composite nesting, parallel regions, active-state highlighting, and - cross-hierarchy transitions lifted to the composite level with a - `[lifted: ...]` marker. diff --git a/changelog.d/sui-t36.5.md b/changelog.d/sui-t36.5.md deleted file mode 100644 index 3247c5a..0000000 --- a/changelog.d/sui-t36.5.md +++ /dev/null @@ -1,8 +0,0 @@ -### Added - -- `StatifierUI.EventLog.build/1` folds a trace message stream into a log - grouped by `(macrostep, round)`, ordered by the producer's stamps rather - than arrival, and `StatifierUI.EventLog.Markdown.render/2` renders it as - collapsible Markdown for `Kino.Markdown`, with wire-format indexes - resolved to state and transition names by - `StatifierUI.EventLog.Labels`. diff --git a/changelog.d/sui-t36.6.md b/changelog.d/sui-t36.6.md deleted file mode 100644 index c923472..0000000 --- a/changelog.d/sui-t36.6.md +++ /dev/null @@ -1,9 +0,0 @@ -### Added - -- `StatifierUI.EventInjection.build/1` turns an `ADR-0003` fixture bundle - (or `nil`) into the event-injection pane model: a sorted palette of - editable event buttons via `StatifierUI.EventInjection.Palette`, a - `free_form_only?` flag for the fixture-less degraded mode, and - `send/2`/`send_draft/3` to deliver a `StatifierUI.EventInjection.Draft` - through `Statifier.Session.send_event/2` - the ordinary recordable input - path, per statifier ADR-0029. diff --git a/changelog.d/sui-t36.7.md b/changelog.d/sui-t36.7.md deleted file mode 100644 index 5f60979..0000000 --- a/changelog.d/sui-t36.7.md +++ /dev/null @@ -1,9 +0,0 @@ -### Added - -- `StatifierUI.DatamodelExplorer.build_authoring/3` and `build_live/2` build - a read-only datamodel tree - document `` declarations, spec - 5.10.1 system variables, predicator provider functions in scope, and - either a fixture scenario or a live session's datamodel with entries - marked `changed?` per macrostep - and - `StatifierUI.DatamodelExplorer.Markdown.render/2` renders it as Markdown - for `Kino.Markdown`. diff --git a/changelog.d/sui-t36.8.md b/changelog.d/sui-t36.8.md deleted file mode 100644 index db35b66..0000000 --- a/changelog.d/sui-t36.8.md +++ /dev/null @@ -1,16 +0,0 @@ -### Added - -- `StatifierUI.Kino.inspect/3` assembles the Livebook inspector: the - configuration diagram, datamodel explorer, event injection, and event - log panes composed over one shared subscriber, live-updating, detaching - cleanly on cell re-evaluation. Compiled only when the optional `:kino` - dependency is present. -- `StatifierUI.Trace.Subscriber.attach/3` accepts `catch_up: true`: on a - session started with `record: true` the missed prefix is replayed into - the buffer atomically with the subscription (statifier ADR-0049); an - unrecorded session falls back to live delivery with a `:not_recorded` - diagnostic the inspector surfaces as "Live-only". -- `StatifierUI.Inspector` - the pure pane-assembly fold the Kino shell - renders, usable by any other frontend. -- `notebooks/inspector.livemd` - the demo notebook, doubling as the - milestone's manual acceptance test. diff --git a/mix.exs b/mix.exs index f6d2680..d1304d7 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule StatifierUI.MixProject do use Mix.Project - @version "0.1.0-dev" + @version "0.1.0" @source_url "https://github.com/riddler/statifier-ui" def project do @@ -15,6 +15,8 @@ defmodule StatifierUI.MixProject do name: "StatifierUI", description: "UI components for authoring, observing, and debugging statifier statecharts", source_url: @source_url, + docs: docs(), + package: package(), test_coverage: [tool: ExCoveralls], preferred_cli_env: [ coveralls: :test, @@ -33,12 +35,47 @@ defmodule StatifierUI.MixProject do defp elixirc_paths(:test), do: ["lib", "test/support"] defp elixirc_paths(_env), do: ["lib"] + # Hexdocs configuration. These paths are read off the publisher's disk at + # `mix docs` time and need no entry in package()'s files: list - the docs + # tarball hexdocs hosts is built separately from the package tarball + # `mix deps.get` fetches. + defp docs do + [ + name: "StatifierUI", + source_ref: "v#{@version}", + canonical: "https://hexdocs.pm/statifier_ui", + source_url: @source_url, + main: "readme", + extras: + [ + "README.md", + "CHANGELOG.md", + "docs/architecture.md", + "docs/wire-format.md", + {"docs/adr/README.md", [title: "Architecture Decision Records", filename: "adr-index"]} + ] ++ Enum.sort(Path.wildcard("docs/adr/0*.md")), + groups_for_extras: [ + Guides: ~r{docs/(?!adr)}, + "Architecture Decision Records": ~r{docs/adr} + ] + ] + end + + defp package do + [ + name: "statifier_ui", + licenses: ["MIT"], + files: ~w(lib mix.exs README.md LICENSE CHANGELOG.md), + links: %{ + "GitHub" => @source_url, + "Changelog" => "#{@source_url}/blob/main/CHANGELOG.md" + } + ] + end + defp deps do [ - # Statifier is not published to hex yet, so the engine is a git dep and - # mix.lock pins the SHA. This becomes a version requirement the day - # statifier publishes; nothing else here depends on which form it takes. - {:statifier, github: "riddler/statifier-ex"}, + statifier_dep(), # Both integrations are optional: the package is a component library, and # a Livebook host has no reason to pull LiveView, or the reverse. Anything @@ -56,4 +93,14 @@ defmodule StatifierUI.MixProject do {:doctor, "~> 0.23", only: :dev, runtime: false} ] end + + # Export STATIFIER_PATH to point at a local checkout while co-developing a + # change that spans both repos. It is an env var rather than a mix.exs edit + # so the override never lands in a commit by accident. + defp statifier_dep do + case System.get_env("STATIFIER_PATH") do + nil -> {:statifier, "~> 2.0"} + path -> {:statifier, path: path, override: true} + end + end end diff --git a/test/statifier_ui/trace/normalizer_test.exs b/test/statifier_ui/trace/normalizer_test.exs index 8e866c8..b6dadb2 100644 --- a/test/statifier_ui/trace/normalizer_test.exs +++ b/test/statifier_ui/trace/normalizer_test.exs @@ -370,7 +370,8 @@ defmodule StatifierUI.Trace.NormalizerTest do macrostep: 1, microstep: 0, id_from_author?: false, - round: 0 + round: 0, + ordinal: 1 } assert {:ok, %Message{type: "effect.send_delayed"} = message} = @@ -391,7 +392,8 @@ defmodule StatifierUI.Trace.NormalizerTest do owner: {:transition, 0}, macrostep: 1, microstep: 0, - round: 0 + round: 0, + ordinal: 1 } assert {:ok, %Message{type: "effect.cancel"} = message} = @@ -1012,7 +1014,8 @@ defmodule StatifierUI.Trace.NormalizerTest do macrostep: 1, microstep: 0, id_from_author?: true, - round: 0 + round: 0, + ordinal: 1 } end @@ -1023,7 +1026,8 @@ defmodule StatifierUI.Trace.NormalizerTest do owner: {:transition, 0}, macrostep: 1, microstep: 0, - round: 0 + round: 0, + ordinal: 1 } end From 621a19f2e43c1f984a27ea7d5f1f6d4f2b9b4795 Mon Sep 17 00:00:00 2001 From: JohnnyT Date: Sat, 22 Aug 2026 18:54:22 -0600 Subject: [PATCH 2/2] Locks statifier 2.0.0 from Hex statifier 2.0.0 is live on Hex, so mix.lock's engine entry moves from the git pin to the Hex release, and the gate runs green against it with no STATIFIER_PATH override - the pre-merge step the release PR named. Refs: sui-tx0 --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 61f2e50..5c0225f 100644 --- a/mix.lock +++ b/mix.lock @@ -26,7 +26,7 @@ "plug_crypto": {:hex, :plug_crypto, "2.2.0", "144014737daaf485407f5ed77daeaad74d651b216a28c87543f8cc7043f8efc8", [:mix], [], "hexpm", "83a95744ab1c75876542b6fab135fcc176280e0f301a111c1f757fddcec95d2c"}, "predicator": {:hex, :predicator, "9.0.0", "c15685ce74e249195df9bbaedf7593c50339ac75b8700a3b13f56cfbcc837ff7", [:mix], [], "hexpm", "8e31739e3d448f9f5d91eb716498a0dfe26f3b27c8bcf96bc6b2ce93c0087e95"}, "saxy": {:hex, :saxy, "1.6.1", "742eff28f553c066d0b54e84662dbf384a1d1f38595472ed15f6e0a33038bbe1", [:mix], [], "hexpm", "8989d504424ba29460a61950f8968380651413fa05e63b6118084db057da1a6b"}, - "statifier": {:git, "https://github.com/riddler/statifier-ex.git", "1d0c6ba18e48cfb6bec3f866702cf15993bbdff7", []}, + "statifier": {:hex, :statifier, "2.0.0", "826cd183113f83cb91f90f41b9e711b7df735219783d996e8ff4a48a40d96c93", [:mix], [{:predicator, "~> 9.0", [hex: :predicator, repo: "hexpm", optional: false]}, {:saxy, "~> 1.6", [hex: :saxy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.3", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8026d01db517058c01cf289f428e9a5c3c4c52ae092ddf698aefd5c4be263a50"}, "table": {:hex, :table, "0.1.2", "87ad1125f5b70c5dea0307aa633194083eb5182ec537efc94e96af08937e14a8", [:mix], [], "hexpm", "7e99bc7efef806315c7e65640724bf165c3061cdc5d854060f74468367065029"}, "telemetry": {:hex, :telemetry, "1.4.2", "a0cb522801dffb1c49fe6e30561badffc7b6d0e180db1300df759faa22062855", [:rebar3], [], "hexpm", "928f6495066506077862c0d1646609eed891a4326bee3126ba54b60af61febb1"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},