Documents, revisions, charts and executions, and why blocks compile to a chart
Copy Markdown View SourceThis page explains the four nouns this package and its docs use - document, revision, chart and execution - what each one is for, and why the package compiles blocks to a chart rather than editing the chart directly. It describes how the pieces relate; the how-to guides cover doing the work.
The running example is patron registration: a visitor becomes a library patron through a few screens, and a library card is issued when the visitor is old enough for one.
Why blocks compile to a chart
Statecharts are the right execution model for long-running workflows, and SCXML is the right interchange format for them. Neither is something a non-engineer will author by hand. This package is the authoring layer in between: a person composes typed blocks, and the package turns what they composed into SCXML that Statifier can run.
That gives the package two artifacts with two different jobs. The document is a tree of typed blocks, each block a unit with a declared shape rather than free-form XML. It is the source of truth that gets edited and stored. The chart is what the compiler emits from it, the form the engine runs.
The direction is deliberate. Documents compile to charts, and nothing decompiles a chart back into blocks. Editing happens on the document, where each block has a declared shape; the chart is derived from it. So that a position in a running chart is not a dead end, the compiler also carries a provenance map: it lets a runtime position be pointed back at the block that produced it.
The four nouns
Each noun has one job, and the same four are used by the engine's persistence guide.
| Noun | What it is here | Identity | Owner | Changes when |
|---|---|---|---|---|
| Document | The block tree an author edits, under one stable id | The id of a %StatifierBlocks.Document{} | The host's authoring store | Never |
| Revision | One saved edit of a document | The document id plus the ordinal in the document's revision field | The host's revision store; this package stores nothing | Every saved edit |
| Chart | The SCXML StatifierBlocks.Compiler.compile/3 emits for a revision | chart_identity: the SHA-256 hash of the SCXML bytes, plus the document id as :chart_name and a nil :chart_version (Statifier.Machine.Identity, the engine's ADR-0052) | The engine | A byte of the SCXML changes |
| Execution | One instance of a chart a host starts | A minted id, pinned to one chart identity | The persistence package or the host | Re-pinned only by an explicit migration |
In patron registration, the registration flow is one document. Each time an author saves an edit, the host keeps a new revision of it. Compiling a revision gives a chart. Each visitor who starts registering is an execution of one chart.
The "Changes when" column is the useful one. A document never changes identity, however many revisions it collects. A revision changes on every saved edit. A chart changes only when a byte of its SCXML does, which is why two revisions can share a chart. An execution does not change chart at all unless the host migrates it.
The record that joins a revision to a chart
StatifierBlocks.CompilationRecord is the bridge between a revision and a
chart. Every successful compile returns one as compiled.record. It holds
the revision's side (document_id, revision, and document_hash, the
SHA-256 of the revision's canonical bytes), the compile inputs
(palette_hash, compiler_version) and the chart's side
(chart_identity).
Neither hash is derived from the other. Two revisions that differ only in
metadata compile to one chart, so the revision's hash and the chart's
identity can disagree about whether two things are the same. The compile
therefore emits the join as a fact instead of leaving a reader to infer it.
An execution names its chart identity. Looking the record up by
chart_identity gives back the document, and the revision or revisions that
compiled to that chart.
Three more parts of the compiled artifact
Three more parts of what a compile returns belong to the same nouns:
acceptsis a document field, so each revision carries its own list: the names of the external events it declares its chart accepts. The compile passes it through ontocompiled.acceptsandcompiled.record.acceptswithout judging it. It never reaches the SCXML, so a revision that edits onlyacceptscompiles to the same chart. Whether the chart can take each name is a publish-time question, answered by the fourth check in How to publish a document.interfaceon%StatifierBlocks.Compiled{}is what the chart offers a parent that invokes it - the outcomes and done-data keys its root declares - and what it relies on from each document it names. A child is named by document id, never by chart: which revision, and so which chart, an id resolves to is the host's resolver's answer.interfaceadds nothing to the SCXML and is not on the record.StatifierBlocks.Publish.findings/3judges one revision - the document the host is about to publish - and emits no SCXML, so it runs before any chart for that revision exists. The host refuses to publish the revision on an:error.
What publishing does and does not touch
Publishing makes a chart and its record, and touches no execution. An execution already in progress stays pinned to the chart it started on. Moving it to another chart is an explicit migration a host performs, never a side effect of a publish, and nothing in this package migrates one.
In patron registration, that means a visitor halfway through the screens keeps the chart they started on when the library publishes a new revision of the registration document. The new revision compiles to a chart of its own; the visitor's execution is not moved to it.
Where to go next
- How to build a block document and compile it to a chart shows the compile and its record in running code.
- How to publish a document lists the checks a host's publish step makes before it keeps a revision.