@olierjs/fugue-cli 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -2
- package/THIRD_PARTY_NOTICES +52 -1
- package/dist/darwin-arm64/fugue-cli +0 -0
- package/dist/darwin-x64/fugue-cli +0 -0
- package/dist/linux-arm64/fugue-cli +0 -0
- package/dist/linux-x64/fugue-cli +0 -0
- package/dist/win32-arm64/fugue-cli.exe +0 -0
- package/dist/win32-x64/fugue-cli.exe +0 -0
- package/package.json +2 -1
- package/skills/fugue-show-me/SKILL.md +44 -0
- package/skills/fugue-show-me/assets/diagram.css +77 -0
- package/skills/fugue-show-me/references/pele.md +68 -0
- package/skills/fugue-show-me/scripts/render-html.mjs +116 -0
- package/skills/show-me/SKILL.md +128 -0
- package/skills/show-me/agents/openai.yaml +2 -0
- package/skills/statechart/SKILL.md +36 -0
- package/skills/statechart/scripts/bug-states.mjs +81 -0
- package/skills/statechart/scripts/chart-edges.mjs +68 -0
- package/skills/statechart/scripts/chart-parts.mjs +21 -0
- package/skills/statechart/scripts/chart-to-tla.mjs +205 -0
- package/skills/statechart/scripts/compare-charts.mjs +74 -0
- package/skills/statechart/scripts/lib.mjs +970 -0
- package/skills/statechart/scripts/map-view.mjs +92 -0
- package/skills/statechart/scripts/property-loop.mjs +60 -0
- package/skills/statechart/scripts/read-check.mjs +79 -0
- package/skills/statechart/scripts/regions-chart.mjs +307 -0
- package/skills/statechart/scripts/render.mjs +531 -0
- package/skills/statechart/scripts/rule-book.mjs +84 -0
- package/skills/statechart/scripts/second-replay.mjs +122 -0
- package/skills/statechart/scripts/snapshot.mjs +74 -0
- package/skills/statechart/scripts/spec-text.mjs +126 -0
- package/skills/statechart/scripts/statechart-help.mjs +350 -0
- package/skills/statechart/scripts/statechart.mjs +903 -0
- package/skills/statechart/scripts/test/band.mmd +15 -0
- package/skills/statechart/scripts/test/regions.mmd +62 -0
- package/skills/statechart/scripts/test/statechart.test.mjs +6854 -0
- package/skills/statechart/scripts/test/sync.mmd +19 -0
- package/skills/statechart/scripts/test/upload.mmd +16 -0
- package/skills/statechart/scripts/tlc-to-chart-args.mjs +52 -0
- package/skills/statechart/scripts/tlc-to-chart.mjs +802 -0
- package/skills/statechart/scripts/tlc-tools.mjs +69 -0
- package/skills/statechart/scripts/ways-in.mjs +69 -0
- package/upstream-skills.json +18 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { CACHE_NAME, PACKAGES } from "./render.mjs";
|
|
2
|
+
|
|
3
|
+
export const USAGE = {
|
|
4
|
+
"from-spec":
|
|
5
|
+
"node statechart.mjs from-spec SPEC.tla [--config FILE] [--fixed FIXED.tla [--fixed-config FILE] | --against AGREED.tla [--against-config FILE]] [--label TEXT] [--map FILE] [--expect-broken RULE]... [--title TEXT] [--out DIR] [--cover] [--png [--theme NAME]]",
|
|
6
|
+
"from-chart":
|
|
7
|
+
"node statechart.mjs from-chart CHART.mmd [--module NAME] [--expect-broken RULE]... [--title TEXT] [--out DIR] [--png [--theme NAME]]",
|
|
8
|
+
render: "node statechart.mjs render CHART.mmd [--theme NAME] [--out FILE]",
|
|
9
|
+
};
|
|
10
|
+
export const usage = (lines) => `Usage: ${lines.join("\n ")}`;
|
|
11
|
+
|
|
12
|
+
const FROM_SPEC = `from-spec SPEC.tla
|
|
13
|
+
Checks each INVARIANT in SPEC.cfg in a separate TLC run. TLC stops at the first rule that a state
|
|
14
|
+
breaks, and the separate runs find the rest. Each run also has the rule as a CONSTRAINT, so TLC
|
|
15
|
+
stops each path at its first state that breaks the rule, and a rule that stays broken reports
|
|
16
|
+
only the step that broke it. SPEC.cfg has SPECIFICATION and one INVARIANT line per rule. Each
|
|
17
|
+
PROPERTY, a rule about whole behaviors, gets one TLC run too, and the charts do not draw it. TLC
|
|
18
|
+
checks a PROPERTY with no [], <>, or ~> only in the initial states, and the command says so. A
|
|
19
|
+
rule listed as both INVARIANT and PROPERTY is checked once, as an INVARIANT. After those runs
|
|
20
|
+
print their results, one more run dumps the state graph and checks every INVARIANT on every
|
|
21
|
+
state in it, and then the command draws the charts. TLC goes down from the next-state action
|
|
22
|
+
through each \\/, \\E, LET, and definition, and names each step after the last definition on that
|
|
23
|
+
path. It stops at any other expression, such as /\\, IF, or CASE, so a definition inside one names
|
|
24
|
+
no step. Give each kind of step its own definition, and join them with \\/ in the next-state
|
|
25
|
+
action. The command warns when steps carry the name of the whole next-state action. The command
|
|
26
|
+
prints the config that it checks each model with, and stops when a config does not exist, with
|
|
27
|
+
the configs beside the spec.
|
|
28
|
+
|
|
29
|
+
--config FILE The config for SPEC.tla. Default: SPEC.cfg next to the spec.
|
|
30
|
+
--fixed FIXED.tla The model of the open fix. It is a copy of the spec in a different file, such
|
|
31
|
+
as SpecFixed.tla or fix/Spec.tla. The command checks it the same way, with
|
|
32
|
+
FIXED.cfg, or with the config of SPEC.tla when there is no FIXED.cfg. The
|
|
33
|
+
charts then mark each bad state fixed or still wrong. A rule that the
|
|
34
|
+
config of FIXED.tla does not check stays as wrong as it is today. When the
|
|
35
|
+
fix shows the same states and steps as a bug of such a rule, the story says
|
|
36
|
+
"does not check this rule".
|
|
37
|
+
--fixed-config FILE
|
|
38
|
+
The config for FIXED.tla.
|
|
39
|
+
--against AGREED.tla
|
|
40
|
+
The spec that SPEC.tla must follow, when SPEC.tla models the code. It is a
|
|
41
|
+
different file from SPEC.tla. The command checks it with AGREED.cfg, and
|
|
42
|
+
never with the config of SPEC.tla. SPEC.tla may add variables, record fields,
|
|
43
|
+
and actions that AGREED.tla does not have. The comparison leaves them out.
|
|
44
|
+
Do not use it with --fixed.
|
|
45
|
+
--against-config FILE
|
|
46
|
+
The config for AGREED.tla, when it is not AGREED.cfg. Give the config that the
|
|
47
|
+
spec's authors say the unchanged spec passes. When the spec shows the same
|
|
48
|
+
states and steps as a bug of a rule that this config does not check, the
|
|
49
|
+
story says "does not check this rule".
|
|
50
|
+
--label TEXT The name of the --fixed or --against model in the charts and the stories.
|
|
51
|
+
Default: "the fix", or "the spec".
|
|
52
|
+
--map FILE The labels for each model state and step. Default: SPEC.map.mjs next to the
|
|
53
|
+
spec. When the file does not exist, the command writes one from the spec's
|
|
54
|
+
VARIABLES and actions to fill in, and exits 2 before it runs TLC. Its
|
|
55
|
+
comments say what each function gets. The map reads a sequence or tuple
|
|
56
|
+
from 1, as in TLA+, so v.seat[1] is its first item. A map that reads from
|
|
57
|
+
0, such as v.pc[r - 1], gets the item before the one it wants, and stops
|
|
58
|
+
the run when it reads index 0.
|
|
59
|
+
--cover Draw only the shortest path from the start to each settled state, plus
|
|
60
|
+
every bug path, in the overview. A state is settled when only the
|
|
61
|
+
mapping's readerActions are enabled. The command prints the states and
|
|
62
|
+
arrows that the overview leaves out. The counts of states that work or
|
|
63
|
+
break a rule still include the states it leaves out. When no step in
|
|
64
|
+
TLC's graph is a reader action, the overview draws every state.
|
|
65
|
+
|
|
66
|
+
With --fixed or --against, SPEC.tla (and FIXED.tla) models the code, and the command finds each
|
|
67
|
+
condition of an action that no comment ties to code with a file and line, as in \\* pay.ts:120.
|
|
68
|
+
A condition is a conjunct of the action that changes nothing and reads a variable that the
|
|
69
|
+
action does not change. A rule can hold only because the model waits for a condition that the
|
|
70
|
+
code does not check, so the command prints these conditions when a rule holds.
|
|
71
|
+
With --against, each story says whether a config beside AGREED.tla expects TLC to break the
|
|
72
|
+
bug's rule. A config's "\\* expect: RULE" line says what TLC finds, and "\\* module: NAME" names
|
|
73
|
+
the module it checks when the folder has more than one .tla file. The command warns when no
|
|
74
|
+
config beside AGREED.tla has an expect line for that module.`;
|
|
75
|
+
|
|
76
|
+
const FROM_CHART = `from-chart CHART.mmd
|
|
77
|
+
Writes NAME.tla, NAME.cfg, and NAME.map.mjs next to the chart. When NAME.tla already exists, it
|
|
78
|
+
writes none of them. Otherwise it replaces a NAME.cfg or NAME.map.mjs that is there. Then it
|
|
79
|
+
checks the spec as from-spec does, and compares the chart that the spec draws with CHART.mmd.
|
|
80
|
+
|
|
81
|
+
--module NAME The TLA+ module name. Default: the chart's file name in CamelCase, such as
|
|
82
|
+
NotesApp for notes-app.mmd.
|
|
83
|
+
|
|
84
|
+
The chart is a Mermaid stateDiagram-v2. Each transition label is one action. For parallel
|
|
85
|
+
regions, split one top-level composite with --, and put each part in a named composite, such as
|
|
86
|
+
state Connection { ... }. Each region is one variable. A step moves every region whose current
|
|
87
|
+
state has a transition with the step's label, and the other regions stay where they are. Each
|
|
88
|
+
rule is one comment line:
|
|
89
|
+
%% never NAME: A & B -- the rule in one sentence
|
|
90
|
+
Each name after the colon is a state caption or a composite, and each comes from a different
|
|
91
|
+
region. A composite stands for every state in it. A "%% when ID, ID" or "%% when not ID, ID" line
|
|
92
|
+
right after a transition is its guard, and the transition's label must end with the guard's
|
|
93
|
+
words in [ ]. The region takes the transition only while each other region that the line names
|
|
94
|
+
is in one of the states that it names, or with not, while none is. A transition with no label is
|
|
95
|
+
its own action. NAME.cfg has SPECIFICATION Spec, INVARIANT TypeOK, and one INVARIANT per never
|
|
96
|
+
line. The command refuses final, choice, fork, and history states, and regions below the top
|
|
97
|
+
level.
|
|
98
|
+
|
|
99
|
+
The comparison looks at reachable states, start states, and labeled steps. A chart with regions
|
|
100
|
+
compares as its combined machine, with each state named by its region states joined by " | ", so
|
|
101
|
+
a guard that blocks a step in only some combinations of regions shows as a difference. The
|
|
102
|
+
guard's words in [ ] are not part of the label. A loop compares like any other step. An edge
|
|
103
|
+
label "a or b" counts as two labels. An edge with no label counts only when no labeled edge joins
|
|
104
|
+
the same two states.`;
|
|
105
|
+
|
|
106
|
+
const RENDER = `render CHART.mmd
|
|
107
|
+
Draws one chart as a PNG. It does not run TLC, so the chart can come from from-spec, from
|
|
108
|
+
from-chart, or from a person.
|
|
109
|
+
|
|
110
|
+
--theme NAME The colors of the PNG: light, dark, or classic. Default: light.
|
|
111
|
+
--out FILE The PNG to write. Default: the chart's path with .png in place of .mmd. The
|
|
112
|
+
command makes the folder when it does not exist.
|
|
113
|
+
|
|
114
|
+
Exit codes
|
|
115
|
+
0 The command wrote the PNG.
|
|
116
|
+
2 The command cannot finish: a bad argument, a chart that it cannot read, a renderer that it
|
|
117
|
+
cannot install or load, or a PNG that it cannot render or write. It writes no PNG then.`;
|
|
118
|
+
|
|
119
|
+
const PNG = `PNG
|
|
120
|
+
Pele lays out each chart, and resvg draws it at twice its size. A chart that is more than 100
|
|
121
|
+
million pixels at that size draws smaller, at about 100 million pixels. The first PNG installs
|
|
122
|
+
${PACKAGES.join(" and ")} with npm into a cache folder. The folder is
|
|
123
|
+
$XDG_CACHE_HOME/statechart/${CACHE_NAME}, or
|
|
124
|
+
~/.cache/statechart/${CACHE_NAME} when XDG_CACHE_HOME is not set. Delete that folder to
|
|
125
|
+
install them again. When npm or the packages fail, the command prints why, exits 2, and writes
|
|
126
|
+
no file.
|
|
127
|
+
The PNG uses the font that fc-match finds for system-ui. The command measures each label in that
|
|
128
|
+
font, so no label goes past its box. When that font is wider than the widths that Pele has built
|
|
129
|
+
in, the command makes the wrap width wider by the same amount, so the lines break where the chart
|
|
130
|
+
breaks them. Without fc-match, or when resvg cannot read that font, the PNG uses Pele's widths.
|
|
131
|
+
Pele puts parallel regions side by side. When a chart with regions has no top-level direction,
|
|
132
|
+
the PNG puts the legend above the regions, so the PNG is narrower.
|
|
133
|
+
--theme picks light, dark, or classic. light is the default. classic keeps the group fills from
|
|
134
|
+
the chart's own classDef lines.`;
|
|
135
|
+
|
|
136
|
+
const COMMON = `Both commands
|
|
137
|
+
--expect-broken RULE
|
|
138
|
+
An INVARIANT or PROPERTY that is known to break. Repeat it, or list rules
|
|
139
|
+
with commas, as in A,B. The command then exits 0 when exactly these rules
|
|
140
|
+
break, and the --fixed or --against model breaks no other rule.
|
|
141
|
+
--title TEXT The chart title. Each bug chart adds ", bug N".
|
|
142
|
+
--out DIR The output folder. Default: NAME-statechart next to the input. A run writes its
|
|
143
|
+
files in a temporary folder, and then replaces the whole output folder with
|
|
144
|
+
it. When runs overlap, the output folder has the files of the run that
|
|
145
|
+
finished last. The command does not use an output folder that has files that
|
|
146
|
+
it did not write. On a filesystem that cannot rename, the command copies the
|
|
147
|
+
files into place, and overlapping runs can mix their files.
|
|
148
|
+
--png Also draw each chart as a PNG. The PNG section tells how.
|
|
149
|
+
--theme NAME The colors of each PNG: light, dark, or classic. Default: light. It needs
|
|
150
|
+
--png.
|
|
151
|
+
-h, --help Print this help, and exit 0.
|
|
152
|
+
|
|
153
|
+
Output
|
|
154
|
+
The command prints whether each rule holds, the chart's counts, and one story per bug.
|
|
155
|
+
from-chart also prints the comparison. The command writes these files in the output folder:
|
|
156
|
+
chart.mmd The overview.
|
|
157
|
+
bug-N.mmd Only the steps of bug N, numbered. Bugs are numbered by fewest reader steps,
|
|
158
|
+
then fewest model steps, then rule name, then cause. Bugs that only the
|
|
159
|
+
--fixed or --against model has come last.
|
|
160
|
+
bug-N-developer.mmd, bug-N-layered.mmd
|
|
161
|
+
The path of bug N through the developer's states, when the mapping exports
|
|
162
|
+
detail(). The layered chart puts each detail state in a box named by its
|
|
163
|
+
state label. A state with a blank detail stays a plain state.
|
|
164
|
+
stories.md One entry per bug: the rule, why it breaks, the steps that lead into it, the
|
|
165
|
+
shortest path in reader steps, and what the --fixed or --against model does
|
|
166
|
+
instead. The rule's sentence is the \\* comment lines above the rule in the
|
|
167
|
+
spec, or in a module in its folder that the spec EXTENDS, directly or
|
|
168
|
+
through another module. With --fixed or --against, the other spec and its
|
|
169
|
+
modules count too, after SPEC.tla and its modules. The entry numbers each
|
|
170
|
+
step that changes the state label, each reader action, each step that
|
|
171
|
+
changes a variable that the rule reads, and the step into the bug. Between
|
|
172
|
+
them, it counts the hidden steps and names each different step once. Above
|
|
173
|
+
the steps, a line names up to five different steps that lead into the bug,
|
|
174
|
+
most common first, and counts the rest. When more than one step or start
|
|
175
|
+
state leads into the bug, the entry says that the path is the shortest that
|
|
176
|
+
TLC found. A step leads into the bug when it goes there from a state that
|
|
177
|
+
keeps the bug's own rule. That state can already break other rules. "(the
|
|
178
|
+
state stays the same)" marks a step on that line that keeps the state's
|
|
179
|
+
label and is not on the bug's path. from-spec does not draw such a step.
|
|
180
|
+
When TLC names the steps only after the whole next-state action, the entry
|
|
181
|
+
cannot count them. Then one line per broken PROPERTY with its sentence,
|
|
182
|
+
which comes only from its own spec file, the reader states that repeat
|
|
183
|
+
forever, if any, and the TLC output that has its counterexample.
|
|
184
|
+
summary.json The counts behind the chart. Each bug has counterexamples, how many model
|
|
185
|
+
states end it, each with its own TLC trace; lastSteps, the steps that lead
|
|
186
|
+
into it, most common first; and startsHere, whether a start state is in it.
|
|
187
|
+
brokenProperties has each broken PROPERTY, its rule sentence, the reader
|
|
188
|
+
states that repeat forever as repeats, and its TLC output. mixedStatus
|
|
189
|
+
lists the states that break a rule in some cases. chartGroups counts the
|
|
190
|
+
drawn states of each group, and mixedGroups lists the states in more than
|
|
191
|
+
one group. With --cover, cover lists the states and arrows that the
|
|
192
|
+
overview leaves out. With --fixed or --against, uncited has the conditions
|
|
193
|
+
with no file and line for each code model. With --against, second.listed
|
|
194
|
+
has what each config beside AGREED.tla expects, and each bug has the
|
|
195
|
+
configs that list it.
|
|
196
|
+
tlc/ The config and the TLC output of each run. When the overview has regions,
|
|
197
|
+
from-spec also writes tlc/regions-all.mmd, the regions with every arrow, and
|
|
198
|
+
tlc/regions-proof.mmd, every state and step of TLC's graph in one region.
|
|
199
|
+
compare-charts.mjs tlc/regions-all.mmd tlc/regions-proof.mmd repeats the
|
|
200
|
+
proof. It prints "same" and exits 0, or prints each difference and exits 1.
|
|
201
|
+
*.png Each chart as a PNG, with --png. A run without --png deletes them.
|
|
202
|
+
|
|
203
|
+
Reading a chart
|
|
204
|
+
from-spec draws the overview as two parallel regions when every state reads "context: progress":
|
|
205
|
+
the contexts on one side, the progress on the other, both at the same time. It does so only when
|
|
206
|
+
that has fewer states, when the regions combine back into exactly every state and step of TLC's
|
|
207
|
+
graph, and when each state's color is the worse color of its two region states. Otherwise it puts
|
|
208
|
+
the states of one context in a box named context, and prints why. Each state in a box shows only
|
|
209
|
+
its progress, and a context with one state stays a plain state.
|
|
210
|
+
Regions draw only the arrows of the shortest way to each state, of each bug path, and of the
|
|
211
|
+
nearest step with each label. The script checks that these arrows make no step that TLC's graph
|
|
212
|
+
does not have. At a step that they make wrongly, it puts back every arrow that the full regions
|
|
213
|
+
take there.
|
|
214
|
+
An arrow from a box's border leaves from every state in the box. The chart draws it only when
|
|
215
|
+
every state in the box takes the step with the same labels to the same state, and no state
|
|
216
|
+
outside the box has the box's name.
|
|
217
|
+
In regions, a step leaves every region that has it. "[when A or B]" or "[unless A or B]",
|
|
218
|
+
whichever is shorter, says which states of the other region the step needs, and a "%% when" or
|
|
219
|
+
"%% when not" line under it lists their ids. A loop says the region may also stay.
|
|
220
|
+
Other words in [ ] come from the mapping's guard(), and "after N s" from its timer(). An edge
|
|
221
|
+
shows guard() words only when every model step behind it has them. In regions, they replace
|
|
222
|
+
"when A or B" and "unless A or B". TLC does not check them, and has no clock, so a reviewer
|
|
223
|
+
checks them against the code.
|
|
224
|
+
Gray states work. Red states break a rule. An orange state breaks a rule in some cases: a model
|
|
225
|
+
state under its label breaks a rule, and a path without a bug reaches another one. A white state
|
|
226
|
+
is reached only after a bug: every path from the start to its model states passes a state that
|
|
227
|
+
breaks a rule, and none of them breaks one. With --fixed, red, orange, and white describe the
|
|
228
|
+
fix, and green states are fixed by it or exist only with it. A purple state breaks a rule only
|
|
229
|
+
with the fix: today it works, is reached only after a bug, or does not exist. With --against, a
|
|
230
|
+
state takes the worse of its two models' colors, red, then orange, then white, and green states
|
|
231
|
+
exist only in the spec.
|
|
232
|
+
When the mapping exports group(), the fill is the state's group, which the legend names. A chart
|
|
233
|
+
state is in a group when all its model states are in it, and a region state when every state
|
|
234
|
+
that it is part of is in it. A state in no group, or in more than one, is gray. The legend lists
|
|
235
|
+
the groups that the chart draws: first the groups in groupColors, in that order, then the others
|
|
236
|
+
from A to Z, and then each status border that the chart uses. groupColors gives a group a light
|
|
237
|
+
"#rgb" or "#rrggbb" color. A group with no color takes the next free palette color: light blue,
|
|
238
|
+
orange, purple, yellow, teal, tan, then pink. A group cannot have the name of a status, such as
|
|
239
|
+
"Breaks a rule". Without group(), the legend shows only the statuses that the chart uses. In the
|
|
240
|
+
.mmd file, the status is a thick border of its color, gray for a white state, on top of the group
|
|
241
|
+
fill. In a PNG, a red, orange, purple, or white state is a box of that color with a border and
|
|
242
|
+
text of its color in place of its group fill, and a fixed state is a green box, so each box shows
|
|
243
|
+
one signal.
|
|
244
|
+
A thick blue border and numbered labels mark the bug. With group(), and in a PNG, the blue border
|
|
245
|
+
marks only the states on the bug's path that work, and the numbered labels show the rest.
|
|
246
|
+
Mermaid state diagrams cannot color an edge.
|
|
247
|
+
Each bug replays on the other model by its state labels and step words, which are what the
|
|
248
|
+
reader sees. The replay takes the bug's own steps. Between them, it also takes the other model's
|
|
249
|
+
steps of actions that SPEC.tla's state graph does not have, when they keep the state label, are
|
|
250
|
+
not reader actions, and change no variable that the broken rule reads. A bug step that the story
|
|
251
|
+
hides must keep the state label on the other model too, unless that model cannot take it. A step
|
|
252
|
+
that the reader sees takes a step with the same words between the same two state labels, and
|
|
253
|
+
prefers a step of the same action. A hidden step of an action that the other model does not have
|
|
254
|
+
is skipped. A step that the reader sees of such an action takes any step to the same shared state
|
|
255
|
+
when it changes a shared variable, or else a step with the same words between the same two state
|
|
256
|
+
labels.
|
|
257
|
+
A label "with the fix, STEP" or "in the spec, STEP" is a step that only the other model takes. On
|
|
258
|
+
a bug's path it leaves the state before the first step that the other model shows the reader
|
|
259
|
+
differently, and ends where that model goes instead. The story quotes the other model's words
|
|
260
|
+
when they differ. "with the fix, STEP cannot happen at step N" or "in the spec, STEP cannot
|
|
261
|
+
happen at step N" loops on the state where that model stops the step, on an arrow of its own. On
|
|
262
|
+
the overview, the label ends with "of bug M". Outside regions, two states otherwise have at most
|
|
263
|
+
one arrow between them. Its lines are the numbered steps, then SPEC.tla's steps, then the other
|
|
264
|
+
model's steps, with a blank line between each group.
|
|
265
|
+
The other model may have other variables, record fields, and actions. The comparison leaves out a
|
|
266
|
+
variable or field that no state of one model has, in its state graph or its traces, and one whose
|
|
267
|
+
values never have the same type in the two models. One mapping draws both models, so it may read
|
|
268
|
+
only what both have. The command stops when the mapping reads, on a state of the other model, a
|
|
269
|
+
variable or field that no state of that model has, or one whose value has a type that SPEC.tla
|
|
270
|
+
never has there. It also stops when the mapping lists the fields of such a state, as a spread,
|
|
271
|
+
Object.keys, or JSON.stringify does. Test "name" in v before the mapping reads a field that the
|
|
272
|
+
other model lacks. The command also stops when a label, guard, group, or cause that the mapping
|
|
273
|
+
makes for the other model has the word "undefined" in it.
|
|
274
|
+
A chart with another model has the line "%% second model (fix|against): LABEL" right after
|
|
275
|
+
stateDiagram-v2. Each chart starts with front matter that keeps Mermaid from shrinking it to the
|
|
276
|
+
page or wrapping its labels again.
|
|
277
|
+
An edge lists every step that takes it, most common first. Words that the steps share at the
|
|
278
|
+
start are written once, as in "you tap Pay or Back".
|
|
279
|
+
|
|
280
|
+
Regenerate
|
|
281
|
+
List every rule that is known to break with --expect-broken, the goals and the known bugs. Each
|
|
282
|
+
run then exits 0 until a rule changes, so one script rebuilds every chart and stops on any change:
|
|
283
|
+
set -e
|
|
284
|
+
node statechart.mjs from-spec Code.tla --against Agreed.tla --expect-broken NoLostUpload --png
|
|
285
|
+
A run that leaves a broken rule out of --expect-broken exits 1, and set -e stops the script there.
|
|
286
|
+
|
|
287
|
+
TLC
|
|
288
|
+
TLA2TOOLS is the path to tla2tools.jar, and JAVA is the path to the java binary. With only
|
|
289
|
+
TLA2TOOLS set, the command runs java from PATH. Otherwise it uses TLC 1.7.4 and Java temurin-25
|
|
290
|
+
from mise, or JAVA when it is set, and installs what is missing with mise. The first install
|
|
291
|
+
downloads about 305 MB and needs the network. It checks the checksum of tla2tools.jar. mise runs
|
|
292
|
+
in an empty temporary folder, so the mise files around the spec, in a repo, or above TMPDIR do
|
|
293
|
+
not apply, do not change, and need no trust. When mise cannot start or fails, the command prints
|
|
294
|
+
why, the install command, and how to set TLA2TOOLS and JAVA instead.
|
|
295
|
+
When the command starts, it copies its input files, which Exit codes lists. TLC checks the
|
|
296
|
+
copies, so an edit during the run does not change what TLC checks.
|
|
297
|
+
TLC checks each rule with one worker per CPU. When a rule is broken, TLC checks it again with one
|
|
298
|
+
worker, which finds the shortest bug paths, the same on every run. The command then prints how
|
|
299
|
+
many times TLC ran and how long that took.
|
|
300
|
+
TLC can find that a rule is broken and then stop on an error, such as a Java exception. The
|
|
301
|
+
command then keeps the paths that TLC printed, marks the rule broken, and warns that the charts
|
|
302
|
+
can miss states that break it. A smaller constant can let TLC finish.
|
|
303
|
+
|
|
304
|
+
Exit codes
|
|
305
|
+
0 Every rule holds, or exactly the --expect-broken rules break. from-chart also draws CHART.mmd
|
|
306
|
+
back unchanged.
|
|
307
|
+
1 Another rule is broken, an expected rule holds, or the chart that the spec draws is not
|
|
308
|
+
CHART.mmd. The output says which.
|
|
309
|
+
2 The command cannot finish: a bad argument, a missing file, a chart or TLC output that it
|
|
310
|
+
cannot read, no TLC, a helper script that cannot start, an output folder with files that the
|
|
311
|
+
command did not write, an input file whose content changed during the run, a new mapping to
|
|
312
|
+
fill in, a mapping that it cannot use, a renderer that --png cannot install or load, or a
|
|
313
|
+
chart that --png cannot render.
|
|
314
|
+
When a chart does not render, the command keeps the files it wrote and exits 2, whatever the
|
|
315
|
+
rules give. Otherwise, exit 2 does not change the output folder, unless the command moved it
|
|
316
|
+
aside and cannot put it back. Then the message names the folder's new path. When the command
|
|
317
|
+
stops after TLC has run, it keeps the files that it wrote, TLC's output among them, in a
|
|
318
|
+
temporary folder, and the message names it. When TLC's state graph is too large to draw, the
|
|
319
|
+
command prints whether each rule holds and exits 2, whatever the rules give. It stops TLC as
|
|
320
|
+
soon as the graph passes that size. The input files are the spec and config, the --fixed or
|
|
321
|
+
--against spec and config, the chart, and the map. Each .tla file in the same folder that an
|
|
322
|
+
input .tla file EXTENDS or INSTANCEs is an input file too, and so is each file that an input
|
|
323
|
+
.mjs or .js file imports by a relative path. Before --png renders, and again before it writes
|
|
324
|
+
the output folder, the command compares the content of each input file with its content at
|
|
325
|
+
the start. A save that does not change the content does not stop the run.`;
|
|
326
|
+
|
|
327
|
+
export const HELP = {
|
|
328
|
+
"": `${usage([...Object.values(USAGE), "node statechart.mjs [from-spec|from-chart|render] --help"])}
|
|
329
|
+
|
|
330
|
+
Draws a Mermaid statechart from TLC's state graph of a TLA+ spec. The spec can model any system,
|
|
331
|
+
such as a product flow, a protocol, a service, a job queue, or an algorithm. A mapping labels each
|
|
332
|
+
state and step in the words of the person who reads the chart, and the help calls that person the
|
|
333
|
+
reader. The reader can be a user, an operator, an engineer, or a reviewer.
|
|
334
|
+
|
|
335
|
+
${FROM_SPEC}
|
|
336
|
+
|
|
337
|
+
${FROM_CHART}
|
|
338
|
+
|
|
339
|
+
${COMMON}
|
|
340
|
+
|
|
341
|
+
${RENDER}
|
|
342
|
+
|
|
343
|
+
${PNG}`,
|
|
344
|
+
"from-spec": `${usage([USAGE["from-spec"]])}\n\n${FROM_SPEC}\n\n${COMMON}\n\n${PNG}`,
|
|
345
|
+
"from-chart": `${usage([USAGE["from-chart"]])}\n\n${FROM_CHART}\n\n${COMMON}\n\n${PNG}`,
|
|
346
|
+
render: `${usage([USAGE.render])}\n\n${RENDER}\n\n${PNG}`,
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
export const more = (cmd) =>
|
|
350
|
+
`Run node statechart.mjs ${cmd ? `${cmd} ` : ""}--help for the flags, the output, and the exit codes.`;
|