Repository navigation
feat(pstack): add the statechart skill to draw TLA+ models as charts - #741
Merged
Merged
Conversation
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.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ 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.
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>
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>
…the comment and assignment cleanups Co-authored-by: lauren <poteto@users.noreply.github.com>
Co-authored-by: lauren <poteto@users.noreply.github.com>
SmailG
added a commit
to SmailG/claude-cursor-plugins
that referenced
this pull request
Oct 22, 2026
…ill to draw TLA+ models as charts (cursor#741) chore: sync with cursor/plugins — feat(pstack): add the statechart skill to draw TLA+ models as charts (cursor#741)
kpeez
added a commit
to kpeez/agent-toolbox
that referenced
this pull request
Oct 22, 2026
…pt rules (#198) ## Summary Sync swe with pstack `d457567..7698375` (cursor/plugins#729, cursor/plugins#741, cursor/plugins#776). swe 1.21.19. - Add `/swe:statechart`, imported verbatim with its Node scripts. All 12 files are class `copied`, so later syncs 3-way merge them. - `typescript-best-practices` now loads on its own: `paths: ["**/*.ts", "**/*.tsx"]` in Claude Code, description match in Codex. It was user-invoked only. - Add `agents/openai.yaml` to both formal-verification skills, so Codex treats them as user-invoked like every other such skill. - Move the pin and the README Credits link to `7698375`. ## Step 4 review - `sharpen/SKILL.md`: skipped. Upstream now returns only the rewritten prompt. `go-deeper` keeps the four questions, runs the rewritten task, and already has a prompt-only mode. - guide 02, guide 10, poteto-help recipes: skipped. Sharpen wording only. - guide 06, poteto-help, README: ported the statechart section and rows. ## Step 5 - `skills/statechart/`: imported (Kyle approved). ## Verification - `pstack_sync.py check`: 184 pinned files covered. - `node --test scripts/test/statechart.test.mjs` with TLC 1.8.0: 66 of 67 pass. One upstream failure: TLC 1.8.0 prints `Temporal property X was violated.`, and `lib.mjs` only matches the older `Temporal properties were violated.` Not patched, because the files stay verbatim. - `claude plugin validate --strict` on the root and `plugins/swe`: pass. - `skill_eval.py validate`: pass. Relative links in changed Markdown resolve. Manifests and `openai.yaml` files parse.
caelaxie
added a commit
to pstack-ports/devin-pstack
that referenced
this pull request
Oct 23, 2026
<!-- CURSOR_AGENT_PR_BODY_BEGIN --> synced to [`4ec7ffa`](cursor/plugins@4ec7ffa). compared `88d4751..4ec7ffa` for `pstack/`. later commits on upstream `main` through [`9b93e81`](cursor/plugins@9b93e81) do not touch `pstack/`. version is `0.15.30` in `plugin.json`, `.devin-plugin/plugin.json`, and the readme marker. ported: - [`eb4db2b`](cursor/plugins@eb4db2b) ([#741](cursor/plugins#741)) add the statechart skill. guide page 6, poteto-help, and the readme skill table match. slash commands keep the `/pstack:` prefix. `disable-model-invocation: true` stays `triggers: [user]`. - [`7698375`](cursor/plugins@7698375) ([#776](cursor/plugins#776)) six fixes to the statechart skill. scripts copied verbatim. - [`4ec7ffa`](cursor/plugins@4ec7ffa) ([#779](cursor/plugins#779)) delete statechart comments that repeat the code, and rename helpers. the version bumps to `0.15.30`. the same bump is in `.devin-plugin/plugin.json` and the readme marker, which is where this repo records the version. skipped: - `harness/pstack/claude-code/` copies in [#741](cursor/plugins#741), [#776](cursor/plugins#776), and [#779](cursor/plugins#779). this port does not carry the generated claude code tree. - [#741](cursor/plugins#741) `pstack/.cursor-plugin/plugin.json`. cursor packaging. the version bump lands in `.devin-plugin/plugin.json` instead. - [#741](cursor/plugins#741) `.github/workflows/validate-plugins.yml` test step. that workflow belongs to the cursor/plugins monorepo and also checks generated harness trees. this repo's ci is separate, so the step was not copied. `node --test skills/statechart/scripts/test/*.test.mjs` passed locally (67 tests, with tlc). harness proof (install, `devin plugins list`, exercising `/pstack:statechart` and `/pstack:poteto-mode`) will follow as a comment. the devin cli can't log in from this cloud environment. <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-00f3aebc-53ad-54ad-9327-63073f2102cb?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-00f3aebc-53ad-54ad-9327-63073f2102cb&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Kaitian Xie <caelaxie@users.noreply.github.com>
poteto
added a commit
that referenced
this pull request
Oct 23, 2026
…nderer (#791) * feat(pstack): bring the statechart skill up to date, with the Pele renderer Port 49 changes from the source copy of the skill. PNGs come from Pele and resvg in place of the Mermaid CLI, TLC installs with mise when TLA2TOOLS is not set, and the OutputFolder model is removed. SKILL.md keeps the wording from #741 for where a spec lives. * fix(pstack): let the statechart regions proof leave out self-loops when the overview does productOf now records combined self-loops, but the flat graph that the regions proof compares against leaves them out unless --loops is set. A region with a stay-or-leave loop on one label then gave a false difference, and from-spec declined parallel regions. Pass the loops setting through productOf, compareCharts, and regionsChart. The proof chart marks "%% self-loops left out" so that compare-charts.mjs compares it the same way. Co-authored-by: lauren <poteto@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Why
A TLA+ model is hard to review for anyone who does not read TLA+. The new
/statechartskill 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-specchecks each rule and draws the charts.--fixedshows what a fix changes, and--againstaudits code against an existing spec.from-chartwrites a TLA+ skeleton from a chart that the PM agreed to.scripts/model/OutputFolder.tla, with its.cfgand 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.poteto-helplist the skill. The harness tree gets its generated copy, and the pstack version goes from 0.15.29 to 0.15.30.validate-plugins.ymlruns the skill tests withnode --test.Scope
TLA2TOOLSandJAVA. WithoutTLA2TOOLS, they say where to downloadtla2tools.jarand exit 2.chart.pngdoes not stay behind.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 --teststep.Verification
Naturals.tladirectly 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, andlint-skill-frontmatterpass.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" --pngfinds the one path that breaks the rule and draws both charts. These scripts write the samechart.mmdandbug-1.mmdthat the images show.