Vertep

Article the third

Two hands on one chart

A statechart is a strange thing to design alone. Its shape is visual: boxes inside boxes, arrows that cross levels, regions that run side by side. But its truth is textual: guards, assignments, event names, the datamodel. People are good at the first and tired by the second. A coding agent is the other way round. This article is about letting each do its half, on one file, with Vertep as the referee.

The arrangement

Three parties work on the same chart file, and none of them owns a private copy of it.

This works because of one decision in Vertepnyk: the text file is the source of truth. The designer does not keep a model of its own and export it. Every drag, rename or new transition on the canvas is a minimal edit to the file, preserving comments and formatting. So when Claude edits the file, the canvas follows; when you edit the canvas, Claude sees an ordinary diff.

Setting up

  1. Install the Vertepnyk extension: code --install-extension pavlyshyn.vertepnyk-vscode, from the VS Code Marketplace, or from Open VSX for Cursor and VSCodium.
  2. Install the vt command, so the agent can check what it writes: cargo install vertep-cli.
  3. Run Claude Code in the project, in the editor's terminal or through its VS Code extension.
  4. Turn on Files: Auto Save. The designer and the agent meet on disk, and a saved file means neither overwrites the other's unsaved work.

Then tell the agent the house rules once, in the project's CLAUDE.md:

# Charts

- Charts live in `charts/` as SCXML (`*.scxml`) or vertep YAML (`*.vt.yaml`).
- After every edit to a chart, run `vt validate <file>` and `vt typecheck <file>`
  and fix what they report before answering.
- Report the chart version from `vt hash <file>` when behaviour changes.
- Never edit `vl:` attributes (SCXML) or the `layout:` key (YAML): that is the
  designer's layout. Leave them where they are when you move or rename states.
- Keep comments. Prefer small edits over rewriting the file.

The fourth rule matters. Layout belongs to the person at the canvas, and an agent that regenerates a file from scratch would throw it away.

The loop

1. Say what should happen

Start from words, not boxes:

Write charts/refund.scxml on the CEL datamodel: a refund agent.
It starts idle; a `request` event carries an amount and moves it to review.
In review a policy chooses approve, reject or escalate. Approval is only
possible up to 100; above that the only ways out are reject or escalate
to a manager, who approves or rejects. Make review and manager decide
states for a decider called `policy`. Validate it.

Claude writes the file, runs vt validate, and answers with the chart's version IRI:

charts/refund.scxml: ok: urn:vertep:chart:sha256:c6282b762c855df3792c2fea39fc345df6ed5e317484461e5d0a7f8d68f292aa

The interesting part of what it wrote is short. The rules are in the chart, not in the decider: with an amount over 100, approve is simply not offered in review, whatever the decider would like.

<state id="review" vt:decide="policy" vt:candidates="approve reject escalate">
  <transition event="approve" cond="ctx.amount &lt;= 100" target="approved"/>
  <transition event="reject" target="rejected"/>
  <transition event="escalate" cond="ctx.amount > 100" target="manager"/>
</state>

2. Look at it

Open the file with Open With… › Vertepnyk Designer and run Vertepnyk: Open Text Editor to the Side. Now you have the canvas and the text side by side. Problems from Vertep's validator appear in the Problems panel, and selecting one reveals the element on the canvas.

This is where people earn their place. A chart that validates can still be wrong, and the canvas shows it in a way the text does not: a state with no way out, two arrows that should have been one transition out of a parent, a manager state that ought to be inside a compound escalated state with its own timeout. Arrange the boxes until the picture tells the story. That arrangement is saved as layout in the file, and it is yours.

3. Run it

Start the simulation. Send request with {"amount": 250} and watch review light up. The enabled events are reject and escalate; approve is not among them. Step back, fork the run, and send {"amount": 40} instead. The simulator runs Vertep's interpreter, so this is not a sketch of the behaviour; it is the behaviour.

Runs are where misunderstandings surface. Perhaps you expected a rejected refund to be re-requestable, and the chart ends in a final state. Say so, in the terms the simulator showed you:

In the simulator, after `reject` the chart stops in `rejected` and nothing
is enabled. A customer should be able to send a new `request` from
`rejected`, with the new amount. Keep my layout.

4. Let the agent edit, and watch the canvas follow

Claude adds the transition and validates again. Because the file is the truth, the designer redraws from the saved text: the new arrow appears, and your arrangement stays. Had the canvas held unsaved changes, VS Code would have reported a conflict on save, which is why auto-save is on.

Edits flow the other way too. Rename manager to supervisor on the canvas, and the file changes exactly where that id appears: the state and the transitions that target it. Ask Claude to "follow the rename into the decider configuration and the tests", and it reads the change with git diff like any other.

5. Check what changed, not just that it is valid

A chart's version IRI is the hash of its behaviour, not of its bytes. That gives code review a sharp question: did this change alter what the chart does? Ask the agent to report vt hash before and after. A rename of a state, a new transition or a changed guard gives a new version; reflowed text and comments do not. When the version changes, the next step is to turn the runs you just made into tests, so the behaviour you checked by hand stays checked.

Layout lives in a namespace Vertep reserves for formatting, so moving a box never changes the version. That holds from vt 0.1.4; an older vt reads the layout as content and reports a new version after a drag, so make sure the agent's vt is current (cargo install vertep-cli).

Who is good at what

After a few rounds a division of labour settles in.

It is the same idea as the goat that does not choose the play, one level up. There, a language model picks among the moves a chart offers while the chart runs. Here, a coding agent proposes changes to the chart itself, and the chart's own checker and simulator decide whether the result is a play worth staging. Nobody, human or model, cuts a slot that the stage cannot hold.

Small habits that help

Install the designer from the Vertepnyk page; the chart used here is fixtures/refund.scxml in its repository.