Skip to content

feat(pstack): add the statechart skill to draw TLA+ models as charts - #741

Merged
poteto merged 9 commits into
mainfrom
lauren/statechart-skill-0e3e
Oct 22, 2026
Merged

poteto merged 9 commits into
mainfrom
lauren/statechart-skill-0e3e

Conversation

@poteto

@poteto poteto commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Why

A TLA+ model is hard to review for anyone who does not read TLA+. The new /statechart skill draws the states that an app's user sees as a Mermaid statechart, built from the state graph that TLC explores. The chart and the checked model are one object, so a PM or a designer can judge the design from the chart.

What changed

  • pstack/skills/statechart/ adds the skill, a shared library, four scripts, and 58 unit tests. statechart.mjs from-spec checks each rule and draws the charts. --fixed shows what a fix changes, and --against audits code against an existing spec. from-chart writes a TLA+ skeleton from a chart that the PM agreed to.
  • scripts/model/OutputFolder.tla, with its .cfg and mapping, models two runs that share one output folder. A test draws its chart and checks that its five rules hold. Five mutants each remove one guard from the model, and the test checks that each mutant breaks the rule that its guard protects.
  • The pstack README, the verify-and-ship guide, and poteto-help list the skill. The harness tree gets its generated copy, and the pstack version goes from 0.15.29 to 0.15.30.
  • validate-plugins.yml runs the skill tests with node --test.

Scope

  • The scripts find TLC only through TLA2TOOLS and JAVA. Without TLA2TOOLS, they say where to download tla2tools.jar and exit 2.
  • Each run works in its own temporary folder and replaces the output folder with one rename at the end. A refused run, or a run whose spec changed while TLC read it, leaves the output folder as it was.
  • A run does not overwrite or delete a file that it did not write. It exits 2 and names the file instead.
  • Runs that overlap on one folder all finish, and the last one to finish owns the folder. A rerun replaces the whole folder, so an old chart.png does not stay behind.
  • CI has no TLC, so the 31 tests that need it skip there. Two of them also need a C compiler, and one of those also needs unprivileged unshare. Each skips when its tool is missing.

Blast Radius

The skill is new, and nothing outside it imports its scripts. The CI change adds one node --test step.

Verification

  • With TLC 1.7.4, all 58 skill tests pass and none skip. Without TLC, 27 pass and 31 skip. The harness copy is byte for byte the same as the skill folder.
  • With a TLC nightly build, 57 of 58 pass. The one failure is a test that expects Naturals.tla directly in the run's own TLC temp folder. The nightly writes it one folder deeper, still inside that temp folder. The scripts work, and only the test's path check is too strict.
  • validate-plugins.mjs, check-pstack-docs.mjs, generate-pstack-harness-trees.mjs --check, quick-validate, and lint-skill-frontmatter pass.
  • A scan for internal paths, names, and model ids finds 0 hits in the patch and in this description.

The charts below come from the test chart, an invented notes app with one rule. The badge never shows Synced while the app is offline. statechart.mjs from-chart notes-app.mmd --expect-broken NeverSyncedWhileOffline --title "Notes app sync" --png finds the one path that breaks the rule and draws both charts. These scripts write the same chart.mmd and bug-1.mmd that the images show.

Notes app overview chart. Offline and Synced is the one red state.

Notes app bug 1. The network drops, then the server confirms, and the badge reads Synced while offline.

Open in Web Open in Cursor 

Draws what a user sees as a Mermaid statechart generated from a TLA+
model. TLC checks the reader's rules as invariants. Each state is marked
works, still wrong, or fixed by an open change, and each counterexample
class is drawn as a numbered path. The skill also turns an agreed chart
into a TLA+ skeleton and compares the drawn chart with the agreed one.

Bumps pstack to 0.15.30 and regenerates the Claude Code harness tree.
- Check each rule in its own TLC run. The generated cfg names no rule,
  and a comment in it lists the rules.
- Refuse TLC output from a run that failed or stopped early, and read
  counterexamples that a start state breaks.
- Count steps that change nothing when a state is settled, number bugs
  that only the fixed model has after the current bugs, and escape
  quotes in captions.
- Compare parallel regions as one machine, and refuse charts that were
  not drawn for comparison.
- Refuse final states and duplicate captions, read class shorthand and
  states first named inside a composite, and match a never line to an
  exact state.
- Rewrite SKILL.md to match, and run the skill script tests in CI.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f149a2d. Configure here.

Comment thread pstack/skills/statechart/scripts/lib.mjs Outdated
cursoragent and others added 3 commits October 21, 2026 03:46
A -- at the top level of a chart opened a second root part that the
parser never read, so every region after it was dropped. chart-to-tla
wrote a one-region skeleton, and compare-charts could print "same".
parseMermaid now refuses the chart. A unit test covers parseMermaid,
chart-to-tla, and compare-charts.

Co-authored-by: lauren <poteto@users.noreply.github.com>
compare-charts let a read or parse error escape, so a chart it could
not read exited 1, the code for charts that differ. It now prints the
file and the reason to stderr and exits 2, as SKILL.md says for a
refused chart. Exit 1 now means only that the charts differ.

Co-authored-by: lauren <poteto@users.noreply.github.com>
Replace the skill, its scripts, and its tests with the current version.
statechart.mjs runs the whole flow with from-spec and from-chart, and its
--help replaces the old Setup section. New features are a code-against-spec
audit (--against), nested state boxes, parallel regions, guards, scoped
charts, timer labels, and legend groups and colors chosen in the mapping.

TLC comes from TLA2TOOLS and JAVA only. Without TLA2TOOLS, the scripts point
to the TLA+ releases page and exit 2.

Co-authored-by: lauren <poteto@users.noreply.github.com>
@cursor cursor Bot changed the title Add the statechart skill to pstack Add the statechart skill to draw TLA+ models as charts a PM can read Oct 21, 2026
cursoragent and others added 2 commits October 21, 2026 21:34
A failed mermaid-cli render printed "could not render" but still exited 0,
or 0 with --expect-broken, so callers reported PNGs that did not exist.
finish() now exits 2 whenever --png renders fewer PNGs than charts, and
keeps the files it wrote. The --help exit codes say so, and a new test
covers it with an npx that cannot launch a browser.

Co-authored-by: lauren <poteto@users.noreply.github.com>
Rendered charts keep their full width. An edge label writes the opening
words that its choices share once, instead of cutting the label with "…".
--full-labels replaces --all-labels, and compare-charts refuses a chart
drawn without it.

The fix replay matches each bug step by the screens and step words that
the reader sees, and two states get at most one arrow. Each rule also runs
as a TLC CONSTRAINT, so a rule that stays broken stops at its first bad
state instead of flooding TLC with counterexamples.

statechart.mjs passes its arguments and models through functions instead
of module globals. The --png exit code from the previous commit is now
part of the source.

Co-authored-by: lauren <poteto@users.noreply.github.com>
@cursor cursor Bot changed the title Add the statechart skill to draw TLA+ models as charts a PM can read feat(pstack): add the statechart skill to draw TLA+ models as charts Oct 22, 2026
cursoragent and others added 2 commits October 22, 2026 05:29
…the comment and assignment cleanups

Co-authored-by: lauren <poteto@users.noreply.github.com>
Co-authored-by: lauren <poteto@users.noreply.github.com>
@poteto
poteto merged commit eb4db2b into main Oct 22, 2026
2 checks passed
@poteto
poteto deleted the lauren/statechart-skill-0e3e branch October 22, 2026 06:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants