Skip to content
Connect an agent
Get started

Migrating from Mermaid

How to move existing Mermaid diagrams to Plotwork with a converter that is deterministic, and how an AI agent finishes the job. It is written for the agent first: every step names the tool to call and what the result means. People can follow the same steps over the HTTP API.

TL;DR

QuestionAnswer
What converts?Flowcharts (flowchart, graph), sequenceDiagram, erDiagram, stateDiagram, classDiagram, gantt, gitGraph, timeline, journey, mindmap, treemap-beta (and treemap), pie, quadrantChart, C4Context, C4Container, C4Component, C4Dynamic, C4Deployment, sankey-beta (and sankey), xychart-beta (and xychart), and treeView-beta (and treeView). Each becomes the Plotwork kind of the same name: flow, sequence, schema, state, class, gantt, git, timeline, journey, mindmap, treemap, pie, quadrant, c4, sankey, xychart, or tree.
What does not?Kanban, radar, and the other kinds in the list below. One diagnostic names the kind and says what to do instead.
One diagramCall convert_mermaid with the Mermaid text. It returns the Plotwork source, the diagnostics, the validation, and an outline.
A whole documentCall sync_markdown with convertMermaid: true. It returns the rewritten document and the picture files to write.
HTTP APIPOST /v1/diagrams/convert-mermaid for one diagram and POST /v1/markdown/sync for a document. Calling them directly needs the Pro plan and an API key.
What is lost?Strokes, text colours, themes, links, tooltips, activations, and a few more. A fill that has a tone is kept as that tone. Every loss is reported with a diagnostic and a hint. Nothing is dropped silently.
Is it a model?No. The converter is plain code. The same Mermaid always gives the same Plotwork.
What is left for the agent?Read every diagnostic, give each node a type that means something, add a title and icons, then validate and render.

Why migrate

GitHub already draws Mermaid fences in the page, so Mermaid works with no setup. Plotwork gives you these in return for the move:

What you getHow
One look in light and darkThe theme owns every colour. There is no classDef, style, or %%{init}%% to vary from one diagram to the next. An author chooses a node type or a tone for meaning.
A static pictureThe output is an SVG file in the repository. It needs no JavaScript, no browser, and no network to display.
Output that diffs cleanlyThe same source gives the same bytes, so a change to a diagram shows as a small change in the diff.
Errors an agent can repairEvery mistake has a line, a column, and a hint, and all of them come back in one pass.
A closed icon vocabularyAn icon is set:name from a fixed list, and an unknown name is an error with a suggestion.
Text that never overflowsBoxes grow to fit their text, with the same padding on every side.

When not to migrate

Keep the Mermaid fence when the diagram is one of the kinds in the next table. GitHub keeps drawing it. A fence that cannot be converted is left exactly as it was, so a document can hold both kinds.

Mermaid kinds Plotwork does not draw

Mermaid diagramClosest Plotwork kindWhat to do
requirement diagramflowRedraw it as a flow diagram with one node per requirement and element, or keep it as Mermaid in its own mermaid fence.
block diagramflowRedraw it as a flow diagram with a group for each block that holds others, or keep it as Mermaid in its own mermaid fence.
packet diagramschemaRedraw it as a schema diagram with one column per field, or keep it as Mermaid in its own mermaid fence.
kanban boardnoneKeep it as Mermaid in its own mermaid fence; GitHub draws it. A Markdown task list per column is the plain alternative.
architecture diagramflowRedraw it as a flow diagram: a group for each Mermaid group and a node with a type and an icon for each service. Or keep it as Mermaid in its own mermaid fence.
ZenUML sequence diagramsequenceRedraw it as a Plotwork sequence diagram (actors, messages, and alt, opt, loop, par blocks), or keep it as Mermaid in its own mermaid fence.
radar chartnoneKeep it as Mermaid in its own mermaid fence; GitHub draws it.

These kinds used to be on this list and now convert to their own kinds: C4 diagrams, class diagrams, Gantt charts, git graphs, mind maps, pie charts, quadrant charts, Sankey diagrams, timelines, tree views, treemaps, user journeys, and XY charts. A state diagram is now a state diagram and no longer a flow diagram. Other Mermaid kinds that convert says where each one is explained.

The migration recipe for an agent

Follow these steps in order. Each one is a tool call or a decision.

  1. Convert. For one diagram, call convert_mermaid with the text inside the fence. For a document, call sync_markdown with convertMermaid: true.
  2. Read every diagnostic. They are data: code, severity, line, column, message, and a hint that says what to do. Lines and columns point into the Mermaid you sent. A code that starts with M1 is an error and nothing was converted. A code that starts with M2 is a warning and the conversion went on.
  3. Resolve each one with the guidance in what does not carry over and the diagnostic codes. Most need no action. Some need a choice, such as which node an edge into a subgraph should reach.
  4. Make it meaningful. The converter chooses a node type only when the shape or a short label says what the node is. Every other node is a plain step. Choose a better type for each (see how a node type is chosen), give the diagram a title, and name the groups. Take the icon suggestions in validation.warnings: each says which icon= to write. Find others with search_icons or map_icons.
  5. Validate. Call draw_diagram on the edited Plotwork source. It returns every error and warning, and draws nothing until the source is valid.
  6. Render. Call draw_diagram again for the pictures, or put the source in a diagram fence and call sync_markdown. See Diagrams in Markdown and on GitHub.

Worked example: one call and its result

The call:

json
{  "tool": "convert_mermaid",  "arguments": {    "source": "flowchart TD\n  user[User] --> db[(Orders DB)]\n  classDef error fill:#f66\n  class db error",    "render": false  }}

The source argument, written out one line at a time:

Mermaid
flowchart TD  user[User] --> db[(Orders DB)]  classDef error fill:#f66  class db error

The result, shortened to the fields an agent reads first:

json
{  "ok": true,  "kind": "flow",  "mermaidType": "flowchart",  "source": "flow direction=down\n\nnode user person \"User\"\nnode db database \"Orders DB\" tone=red\n\nuser -> db",  "diagnostics": [    {      "code": "M214",      "severity": "warning",      "line": 1,      "message": "The diagram has no title.",      "hint": "Add a title to the header line of the Plotwork source, or a title: line in the Mermaid front matter."    },    {      "code": "M204",      "severity": "warning",      "line": 3,      "message": "The fill #f66 of the class \"error\" became the tone red.",      "hint": "Check that the tone says what it means. The theme draws it in light and dark."    }  ],  "stats": {    "nodes": 2,    "edges": 1,    "groups": 0,    "dropped": 0,    "renamed": 0  },  "validation": {    "valid": true,    "errors": [],    "warnings": []  },  "outline": [    "Nodes:",    "- User, person",    "- Orders DB, database",    "Connections:",    "- User to Orders DB"  ],  "next": "Read the 2 diagnostics (they point into the Mermaid) and fix what the hints say. Then choose a meaningful type for every node left as step, add a title, and take the icon suggestions in validation.warnings. Call draw_diagram on the edited source to check and draw it, or sync_markdown to place it in a document."}

The source of the result, written out one line at a time:

Plotwork
flow direction=down
node user person "User"node db database "Orders DB" tone=red
user -> db

The field ok is true only when source passes the Plotwork parser. When it is false, source is empty and diagnostics holds the reason, so the converter never returns source that would fail validation.

Worked example: a whole document

Ask the agent: "Replace the Mermaid diagrams in README.md." The tool sequence is:

StepCallWhat to do with the result
1sync_markdown with markdown (the file text), documentPath: "README.md", convertMermaid: true, mode: "check"A dry run. Read report: each Mermaid fence is converted or failed, with diagnostics. Nothing is rewritten.
2Fix what the report says, or accept itFences that fail are left as Mermaid and reported.
3sync_markdown again with mode: "sync"The result has markdown (the rewritten document) and files (every SVG, light and dark).
4Write markdown to README.md and each files entry to its pathThe tool writes nothing itself; it has no file system.
5Review the change in gitRead the diff before it is committed.

The result, shortened:

json
{  "mode": "sync",  "markdown": "<the whole document, rewritten>",  "changed": true,  "files": [    {      "path": "assets/diagrams/readme-sign-in-flow.svg",      "content": "<svg text>",      "encoding": "utf8"    },    {      "path": "assets/diagrams/readme-sign-in-flow.dark.svg",      "content": "<svg text>",      "encoding": "utf8"    }  ],  "report": [    {      "id": "readme-sign-in-flow",      "source": "mermaid",      "kind": "flow",      "line": 12,      "status": "converted",      "diagnostics": [],      "message": null    }  ],  "summary": {    "diagrams": 1,    "synced": 0,    "converted": 1,    "unchanged": 0,    "skipped": 0,    "failed": 0,    "files": 2  },  "caps": {    "hit": false,    "messages": []  }}

A call handles up to 30 diagrams and a document of up to 10 million characters. If a document has more, caps says which fences were skipped; send them in a second call. Nothing is cut silently.

Concept mapping

These tables are the converter's own data, so they cannot drift from what it does.

Flowchart shapes

Mermaid shapeExamplePlotwork typeWhy
RectangleA[Text]stepThe neutral shape. The label may suggest a type (see the keyword table).
Rounded rectangleA(Text)stepThe same as a rectangle.
StadiumA([Text])terminalA start or an end of a flow, which is what a terminal is.
SubroutineA[[Text]]stepA plain step. Plotwork has no framed box.
CylinderA[(Text)]databaseA data store. A label such as Redis or Kafka picks the more exact cache or queue.
CircleA((Text))terminalUsed for connectors and for starts and ends, so a terminal.
Double circleA(((Text)))terminalAn end of a flow, so a terminal.
RhombusA{Text}decisionA question with branches, which is what a decision is.
HexagonA{{Text}}stepA plain step. Plotwork has no hexagon.
AsymmetricA>Text]stepA plain step.
ParallelogramA[/Text/]stepA plain step. Input and output are not a separate type.
Parallelogram, leaning leftA[\Text\]stepA plain step.
TrapezoidA[/Text\]stepA plain step.
Trapezoid, invertedA[\Text/]stepA plain step.

Shapes written in the newer A@{ shape: name, label: "text" } form map the same way:

Shape names for @{ shape: name }Plotwork typeWhy
lin-cyl, disk, h-cyl, das, horizontal-cylinder, bow-rect, stored-datastorageStorage that is not a database.
cloudexternalSomething outside your system, which is what an external node is.
doc, docs, documents, lin-doc, tag-doc, st-doc, flag, paper-tapestepA plain step.
sm-circ, small-circle, startterminalThe start of a flow.
fr-circ, framed-circle, stopterminalThe end of a flow.
st-rect, stacked-rectangle, lin-rect, lin-proc, lined-process, shaded-process, div-rect, div-proc, divided-process, divided-rectangle, notch-rect, card, notched-rectangle, sl-rect, manual-input, win-pane, internal-storage, curv-trap, display, delay, half-rounded-rectangle, tri, extract, flip-tri, manual-file, hourglass, collate, bolt, com-link, lightning-bolt, cross-circ, summary, f-circ, filled-circle, junction, text, brace, brace-r, braces, fork, join, bang, processes, procs, bow-tie-rectanglestepA plain step with a warning, since the shape itself is lost.

Edges

Edge text written as -->|text| or -- text --> becomes the edge label. A chain such as A --> B --> C and a fan such as A & B --> C & D become one edge for each pair. An edge animation, e1@{ animate: true }, is kept as animate=on on the line. The edge id and the speed are dropped and reported, and a link with no arrowhead stays still, because dashes travel toward an arrowhead.

Mermaid linkPlotwork arrowWhat changes
A --> B->Nothing.
A --- B--Nothing.
A -.-> B-->Nothing.
A -.- B--Plotwork has no dashed line without an arrowhead, so the line is drawn solid.
A ==> B->Plotwork draws every edge with one stroke weight.
A === B--Plotwork draws every edge with one stroke weight.
A --o B->Plotwork has one arrowhead, so a circle or a cross at the end of a link becomes an arrowhead.
A --x B->Plotwork has one arrowhead, so a circle or a cross at the end of a link becomes an arrowhead.
A <--> B<->Nothing.
A <-.-> B<-->Nothing.
A <==> B<->Plotwork draws every edge with one stroke weight.
A o--o B<->Plotwork has one arrowhead, so a circle or a cross at the end of a link becomes an arrowhead.
A x--x B<->Plotwork has one arrowhead, so a circle or a cross at the end of a link becomes an arrowhead.
A ~~~ BnoneDropped with a warning. It is a layout trick, and layout is done for you.

Subgraphs

MermaidPlotworkNote
subgraph id [Title]group id "Title" {, its nodes, and }The title is the label. A group has no direction of its own.
subgraph Title with spacesgroup Title_with_spaces "Title with spaces" {The text is the label, and the id is made valid, with a warning.
Nested subgraphsNested groupsUp to 24 levels deep.
A node mentioned inside a subgraphA node declared inside the groupA node belongs to the first subgraph it is mentioned in. A second subgraph that mentions it gets a warning.
A --> subgraphIdAn edge to a node inside the groupPlotwork edges connect nodes. An edge into a subgraph goes to its first node that nothing inside points at; an edge out of one comes from its last node that points at nothing inside. A warning names the node chosen.
direction TB inside a subgraphDropped with a warningPlotwork has one reading direction for the whole diagram.
An empty subgraphDropped with a warningA group with nothing in it draws nothing worth keeping.
A subgraph with no endClosed at the end of the diagram, with a warningAn extra end is ignored, with a warning.

Sequence diagrams

Arrows:

Mermaid arrowMeaningPlotwork arrowWhat changes
<<-->>Dotted, arrowhead at both ends<-->Nothing.
<<->>Solid, arrowhead at both ends<->Nothing.
-->>Dotted line with an arrowhead: a reply-->Nothing.
->>Solid line with an arrowhead: a call->Nothing.
--xDotted line ending in a cross-->Plotwork has one arrowhead, so a cross at the end of a message becomes an arrowhead. Say what failed in the label.
--)Dotted line with an open arrow: asynchronous..>Plotwork draws an asynchronous message as a dotted line with an arrowhead.
-->Dotted line with no arrowhead--Plotwork has no dashed line without an arrowhead, so the line is drawn solid.
-xSolid line ending in a cross->Plotwork has one arrowhead, so a cross at the end of a message becomes an arrowhead. Say what failed in the label.
-)Solid line with an open arrow: asynchronous..>Plotwork draws an asynchronous message as a dotted line with an arrowhead.
->Solid line with no arrowhead--Nothing.

Blocks:

Mermaid blockPlotwork blockNote
looploopThe same.
altaltThe same.
optoptThe same.
parparThe same.
criticalaltBecomes an alt: the critical region is the first branch and each option is an else branch.
breakoptBecomes an opt, which is the nearest frame for steps that happen only on one path.
rectnoneThe coloured background is dropped and the messages inside are kept.

Everything else:

MermaidPlotworkNote
participant A as Aliceactor a step "Alice"The neutral type step, unless the name says what it is, as Database or Browser do. Choose a type.
actor A as Bobactor a person "Bob"The stick figure means a person.
A messages B without declaring themActors declared in the order they first appearPlotwork needs every actor declared; the converter adds them.
Note over A,B: textnote over a, b : "text"The same.
Note left of A: text, Note right of A: textnote over a : "text"Plotwork notes sit over a lifeline. A warning says the side was lost.
A->>B: text with <br/>A message label on one linePlotwork wraps text itself.
A->>A: texta -> a : "text"A message to oneself is kept.
autonumbernumbered=on on the headerEach message gets a numbered badge. A message before the autonumber line, or after autonumber off, gets step=off.
autonumber 10, autonumber 10 5numbered=on start=10, and step=15 on a message whose number the count would not giveThe numbers Mermaid draws are the numbers Plotwork draws, up to 999.
title TextThe diagram titleA front matter title wins when both are given.

Entity relationship diagrams

A Mermaid relationship names two entities and no columns. A Plotwork relation joins one column to another, so the converter finds the key columns, and adds a column when it cannot. Each added column is reported.

MeaningLeft markerRight markerPlotwork
Exactly one||||The key it points at; the foreign key column is not nullable.
Zero or one|oo|The foreign key column is nullable.
One or more}||{A foreign key on this side (many to one). The "at least one" minimum is not drawn.
Zero or more}oo{A foreign key on this side (many to one).

Whole relationships:

MermaidMeaningPlotwork
A ||--o{ B : hasOne A, zero or more BB holds a foreign key to A.
A ||--|| B : hasOne A, exactly one BB holds a unique foreign key to A, which draws one to one.
A }o--o| B : hasMany A, zero or one BA holds a nullable foreign key to B.
A }o--o{ B : hasMany to manyA join table with a foreign key to each side.

Lines and keys:

Mermaid lineMeaningPlotwork
--IdentifyingA normal relation. Plotwork draws one line style.
..Non-identifyingA normal relation, with a warning that the dotted line is not drawn.
Mermaid keyPlotwork flagMeaning
PKpkPrimary key
FKfkForeign key
UKuniqueUnique key

State diagrams

A state diagram becomes a Plotwork state diagram, which has its own kind: states are rounded cards, transitions are labelled lines, composite states are frames, and the start and end markers, choices, forks, joins and notes are drawn as such. Older versions of the converter turned a state diagram into a flow diagram of groups and terminal nodes, so a state diagram that was converted earlier reads differently when it is converted again. See State diagrams.

MermaidPlotworkNote
A --> B : eventA transition a -> b : "event"The label is kept as written, so event [guard] / action keeps its guard and its action.
state Astate a "A"A rounded card. Give it a tone= for a meaning such as an error or a success.
state "Long name" as Astate a "Long name"The id stays A.
A : descriptionThe state sub= textEach description line stays its own line under the name.
[*] --> Astart -> aThe start marker of the scope the line is in: the diagram, or the composite state.
A --> [*]a -> endThe end marker of the scope the line is in.
state A { inner states }A composite state state a "A" { with its states insideEach composite state has its own start and end markers, and a transition may enter or leave the whole state.
--region { blocks inside the composite stateThe states before the first -- are the first region. A dashed line separates the regions.
state A <<choice>>choice aA pill with a branch icon. Label the branches with their guards.
state A <<fork>> or <<join>>fork a or join aA bar that splits or merges parallel paths.
direction LRdirection=right in the headerOnly at the top level. Inside a composite state it is dropped.
note right of A : textnote a : "text" side=rightA note beside the state, across the reading direction. A note on a composite state sits inside its frame.
classDef, class, :::name, styleDropped with a warningThe theme owns colour. A class name such as error gives the state the tone red.

Other Mermaid kinds that convert

More Mermaid kinds convert into a Plotwork kind of their own. Each has a guide that shows what is kept and what is dropped. The codes in the last column are the ones that only that kind raises, and the diagnostic codes table explains every code.

MermaidPlotwork kindGuideCodes of its own
classDiagramclassClass diagramsM240, M241, M242
ganttganttGantt chartsM130, M131, M132, M230 to M238
gitGraphgitGit graphsM120, M220, M221, M222
timelinetimelineTimelinesM260
journeyjourneyUser journeysM270, M271
mindmapmindmapMind mapsM180, M280, M281, M282
stateDiagramstateState diagramsM250, M251
treemap-beta, treemaptreemapTreemapsNone of its own
piepiePie chartsNone of its own
quadrantChartquadrantQuadrant chartsNone of its own
C4Context, C4Container, C4Component, C4Dynamic, C4Deploymentc4C4 diagramsNone of its own
sankey-beta, sankeysankeySankey diagramsNone of its own
xychart-beta, xychartxychartXY chartsNone of its own
treeView-beta, treeViewtreeTree diagramsNone of its own

Three kinds have no Mermaid type to read: Layered architecture, Matrices, and Swimlane diagrams. Each guide says how to move a Mermaid diagram or a Markdown table to it by hand.

How a node type is chosen

The converter is conservative. It picks a type in this order and stops at the first that applies:

  1. The construct itself decides, as for a flowchart rhombus (a decision) or a stadium (a terminal).
  2. A class name that carries meaning, such as database, queue, external, or error. The converter reports that it used the class.
  3. The shape: a cylinder is a database, a rhombus is a decision, a stadium or a circle is a terminal, a cloud is external. A label keyword may refine a cylinder to a cache, a queue, or storage.
  4. A keyword in a short label of at most three words, and only when exactly one type matches. "Orders DB" is a database; "Redis queue" matches two types and stays neutral; a sentence is never read.
  5. Otherwise the neutral step, which the converter lists in one warning so you choose a better one.

It writes an icon only when the label names a brand. "PostgreSQL", "Redis cache", or a second line such as "Stripe API" gets that brand's mark (icon=simple:postgresql). A decision, a start or end, and a plain step never get one, and a name that is only a word for a kind of thing ("Message Queue") keeps the icon of its type. The Plotwork validator suggests the other icons from the labels, and those suggestions come back in validation.warnings.

An HTTP status code at the start of a label sets a tone when the node has none: 200 and the other 2xx codes are green, and 4xx and 5xx codes are red. "Return 429 Too Many Requests" is red. A 3xx code gets no tone, because a redirect is neither good nor bad.

The words it looks for:

TypeWhole labelLast word of a short labelProduct names in a short label
databasedatabase, databases, db, datastore, data store, sqldatabase, db, datastorepostgres, postgresql, mysql, mariadb, sqlite, mongodb, mongo, dynamodb, cassandra, cockroachdb, oracle, mssql
cachecache, cachescacheredis, memcached, valkey
queuequeue, queues, topic, message bus, event busqueue, topic, buskafka, rabbitmq, sqs, nats, pubsub, kinesis
storagestorage, bucket, buckets, blob storage, file storagebucket, storages3, gcs
personuser, users, customer, customers, admin, administrator, developer, visitor, end user, operator, person, user requestnonenone
clientbrowser, client, clients, frontend, front end, web app, mobile app, ui, cli, spa, webapp, client requestbrowsernone
gatewaygateway, api gateway, load balancer, reverse proxy, proxy, cdn, ingressgateway, balancer, proxynginx, envoy, traefik, haproxy, kong
serviceapi, service, backend, back end, server, microservice, rest api, web server, app serverservice, api, server, microservice, backendnone
functionworker, workers, worker pool, cron, cron job, scheduler, lambda, function, job, batch jobworker, cron, scheduler, lambda, functionlambda
externalexternal, external system, third party, 3rd party, external api, partner, payment provider, email provider, sms provider, payment processornonestripe, sendgrid, twilio, paypal, mailgun, braintree
identityidp, identity provider, sso, auth server, authorization server, oauth, oidcnoneokta, auth0, keycloak
secretsecret, secrets, vault, key vault, kms, secret storenonenone

Class names that give a type or a tone:

Class namesBecomes
database, db, datastore, storetype database
cachetype cache
queue, topic, brokertype queue
storage, buckettype storage
external, thirdparty, ext, vendortype external
user, person, actortype person
client, browser, frontendtype client
service, api, backendtype service
gateway, proxytype gateway
worker, job, function, lambdatype function
decisiontype decision
start, end, terminaltype terminal
error, danger, fail, failure, criticaltone red
warning, warn, cautiontone amber
success, ok, done, pass, passedtone green

Fills become tones

A style line, a classDef line, and a class or :::name that points at one can set a fill. The converter reads the fill and keeps it as the tone of the node (or the group) when the colour is clear: it works out the hue of the colour and takes the tone whose band holds it. Each one is reported with M204, the first few on their own and the rest in one summary, so a diagram painted by an agent is not buried in warnings. A tone is a meaning, and the theme draws it in light and dark.

Hue of the fillToneColours that land there
345 to 15 degreesredred, crimson, tomato
15 to 38 degreesorangeorange, salmon
38 to 68 degreesamberyellow, gold, khaki
68 to 160 degreesgreenlime, green, light green
160 to 200 degreesaquateal, cyan, turquoise
200 to 255 degreesbluesky blue, blue, navy, a periwinkle such as #bbf
255 to 320 degreesvioletpurple, violet, indigo, lavender, a pastel such as #f9f

These fills become no tone, and are reported as dropped styling with M200 along with the strokes, text colours, and widths:

FillWhy it becomes no tone
Greys, such as #ddd and #333A grey is the page's own neutral, so there is nothing to keep.
Near white and near black, such as #fff and #111Neither says a hue; they are the surface and the ink.
Pink and magenta, from 320 to 345 degreesPlotwork has no pink, and the nearest tone would be a guess.
A fill that is mostly transparent, or a colour that cannot be readIt is not a fill an agent chose to show.

A fill set on the node itself wins over a tone a class name such as error suggests.

Edge labels that get a status icon

An edge label that states an outcome gets a status icon, written as icon=status:name on the line. The icon brings its own colour, so a branch such as "Rate limited" says what happened without a colour code to learn. A label matches only when all of it is one of the phrases below, in any case and with its punctuation ignored, so "Sends the order" and "Webhook" are never given an outcome they do not state. The labels yes, no, true, and false get their tick and cross from the language itself, so the converter writes nothing for them.

Status iconEdge labels that get it
status:yesy, ok, okay, pass, passed, valid, correct
status:non, invalid, incorrect
status:successsuccess, successful, succeeded, done, complete, completed, approved, accepted
status:errorerror, errors, failed, failure, fail, rejected, exception, crash, crashed
status:verifiedauthenticated, verified, signed in, logged in
status:allowedallowed, authorized, authorised, permitted, granted, access granted
status:denieddenied, forbidden, unauthorized, unauthorised, unauthenticated, not authenticated, not authorized, not allowed, access denied, permission denied
status:blockedblocked, banned, throttled, rate limited, rate limit exceeded, too many requests
status:warningwarning, warn, degraded
status:expiredexpired, timeout, timed out
status:retryretry, retrying, try again
status:pendingpending, waiting
status:skippedskip, skipped
status:runningrunning, in progress, processing
status:revokedrevoked

What does not carry over and why

Plotwork owns colour, spacing, and layout, so a diagram cannot set them. The rule for every row is the same: choose a node type or a tone for meaning, and let the theme choose the colour.

MermaidApplies toWhy it does not carry overWhat to do instead
classDefflowchart, stateThe theme owns every colour, in light and dark, so a diagram cannot set one.Choose a node type or a tone for what the node means; the theme chooses the colour.
class and :::nameflowchart, stateThe theme owns every colour, in light and dark, so a diagram cannot set one.Choose a node type or a tone for what the node means; the theme chooses the colour.
styleflowchart, stateThe theme owns every colour, in light and dark, so a diagram cannot set one.Choose a node type or a tone for what the node means; the theme chooses the colour.
What a style or classDef line sets besides a fill that has a toneflowchartThe theme owns every colour, in light and dark, so a diagram cannot set one. A fill is kept as the nearest tone; strokes, text colours, widths, and a fill with no tone are left out.Choose a node type or a tone for what the node means; the theme chooses the colour.
linkStyleflowchartPlotwork draws every edge the same way.Use an arrow form for meaning: -> for a call, --> for a reply or optional link, ..> for an asynchronous call.
%%{init}%% directives, themes, themeVariables, curveallTheme and layout settings belong to Plotwork, not to a diagram.Remove it. Use direction= in the header for the reading direction.
Front matter config:allTheme and layout settings belong to Plotwork, not to a diagram.Remove it. A title: is kept as the diagram title.
click callbacks and linksflowchart, stateA Plotwork diagram is a static picture with no script and no links inside it.Link to the page in the surrounding text, or put the name of the target in the node sub text.
TooltipsflowchartA static picture has no hover.Put the detail in the node sub text or in the diagram desc=.
Actor link and linkssequenceA static picture has no menus or links.Link to the page in the surrounding text.
direction inside a subgraphflowchartPlotwork has one reading direction for the whole diagram.Set direction= on the header, or split the subgraph into its own diagram.
Extra dashes in a link, as in ---->flowchartLayout places nodes; an author cannot ask for longer edges.Nothing: the layout spaces nodes for you.
Invisible link ~~~flowchartIt is a trick to move nodes, and layout does that for you.Group the nodes with a group if they belong together.
Edge idsflowchartPlotwork names an edge by the two nodes it joins, so an id has nothing to refer to.Nothing. An id that only animates the edge is not lost: e1@{ animate: true } becomes animate=on on the line.
Animation speeds fast and slowflowchartPlotwork animates every line at one calm pace, so a speed has nothing to set.The line is still animated with animate=on. Say in the label if a flow is meant to be fast.
curve on an edgeflowchartLayout routes every edge, with square bends and rounded corners, so a curve cannot be chosen.Nothing: layout draws the edge for you.
Activations +, -, activate, deactivatesequencePlotwork draws lifelines without activation bars.Say who is busy in the message labels or a note over.
create and destroysequenceEvery Plotwork participant has a lifeline for the whole diagram.Declare the participant at the top and say it is created or destroyed in a note over.
box around participantssequencePlotwork has no frame around participants.Put the grouping in the participant sub text, such as the name of the system.
rect backgroundssequenceThe theme owns every colour, in light and dark, so a diagram cannot set one.The messages inside are kept. Use opt or loop if the highlighted part means something.
State classDef, class, and stylestateThe theme owns every colour, in light and dark, so a diagram cannot set one.A tone= on a state is a meaning, not a colour. A class name such as error or success gives the state that tone, and the class itself is dropped.
Entity styleERThe theme owns every colour, in light and dark, so a diagram cannot set one.A tone= on a table is a meaning, not a colour.
Non-identifying relationships ..ERPlotwork draws one line style for a relation.Mark an optional reference with nullable on the foreign key column.
"One or more" on the many sideERA relation draws many to one; a minimum count is a rule, not a shape.Say it in the relation label if it matters.

Other text is cleaned and the change is reported: HTML tags and Markdown marks are removed, <br/> and \n become a line break (a second line of a node label becomes its sub= text), entities such as #quot; and &amp; are decoded, and Font Awesome tokens such as fa:fa-database are removed with the closest bundled icon named in the hint. Quotes and backslashes are escaped so every label keeps its exact text.

Ids that are not valid Plotwork ids (spaces, dots, accents, a leading digit) are renamed the same way every time, and each rename is reported. The label keeps the original text.

Diagnostic codes

CodeSeverityWhat it meansWhat to do
M100errorThe text is empty or does not start with a Mermaid diagram type.Check that the first line is a diagram type such as flowchart TD or sequenceDiagram.
M101errorPlotwork does not draw this kind of Mermaid diagram.Keep it as Mermaid in its own fence, or redraw it as the closest Plotwork kind, which the hint names.
M102errorThe diagram is over a size limit: too much text, too many nodes or edges, or nesting that is too deep.Split it into several diagrams along a subgraph, then convert each.
M103errorThe diagram has a header but nothing to draw.Add nodes or messages to the Mermaid, or delete the fence.
M104errorThe converter produced Plotwork that did not pass validation, so it returned nothing.Report the Mermaid that caused it. Redraw the diagram by hand in the meantime.
M110errorA statement could not be read as Mermaid, so nothing was converted.Fix the line the diagnostic points at, or convert with lenient to skip unreadable lines and keep the rest.
M200warningStrokes, text colours, classes, themes, or other styling were dropped. A fill that has a tone is not here: it is kept as a tone (M204).Choose a node type or a tone for what a node means. The theme chooses the colour.
M201warningA click action, link, or tooltip was dropped.Link to the page in the surrounding text. A diagram is a static picture.
M202warningA hint about layout, such as a link length or a direction inside a subgraph, was dropped.Nothing. Plotwork places nodes itself. Set direction= on the header if the reading direction matters.
M203warningSomething that carried meaning has no Plotwork form and was dropped, such as activations or state notes.Say it in a label, a sub= text, or a note over if the reader needs it.
M204warningA construct was replaced by the nearest Plotwork one, such as a cross end becoming an arrowhead or a fill becoming the nearest tone.Read the message. Edit the Plotwork if the nearest form says the wrong thing.
M205warningLabel text was cleaned: HTML tags, Markdown marks, or line breaks were removed or moved.Compare the label in the Plotwork with the Mermaid. A second line became the sub= text of a node.
M206warningAn id was not a valid Plotwork id and was renamed.Nothing, unless you refer to the id elsewhere. The label keeps the original text.
M207warningA node type or tone was taken from a class name such as database or error.Check that the type is right. The class itself was dropped.
M208warningNodes got the neutral type step because nothing in the Mermaid says what they are.Choose a type for each node that means something: database, service, queue, person, and so on.
M209warningAn edge repeated an earlier one exactly and was dropped.Nothing. Keep both only if they mean different things, and then give them different labels.
M210warningAn edge pointed at a subgraph, and Plotwork edges connect nodes, so it was redrawn to a node inside.Check that the node named in the message is the one the edge means.
M211warningA relationship names no columns, and Plotwork draws a relation between two columns, so one was added.Rename the added column, or replace it with the real foreign key.
M212warningA many-to-many relationship was written as a join table with a foreign key to each side.Rename the join table, and add the columns it holds.
M213warningA node appeared in more than one subgraph, and Plotwork puts a node in one group.Check that the group the message names is the one you want.
M214warningThe diagram has no title. A title names the picture, its file, and its alt text.Add a title to the header line of the Plotwork source.
M215warningA Font Awesome icon in a label was removed. Plotwork icons are a closed set.Set icon= on the node. The hint names the closest bundled icon.
M216warningAn empty subgraph, state, or entity was dropped, or a label was empty and the id was used.Add content to it in the Mermaid first if it should be drawn.
M217warningA subgraph or block was never closed, or an end had nothing to close, and the converter repaired it.Check where the block should end.
M218warningA statement could not be read and was skipped because the conversion is lenient.Fix the Mermaid line, or add the edge or node by hand in the Plotwork source.
M250warningA note on a composite state was drawn inside its frame, below the states it holds, because the frame has no free side.Check that the note still reads well there. A note on a simple state keeps its side.
M251warningA state was named start or end, which Plotwork keeps for the start and end markers, so it was renamed.Nothing, unless you refer to the id elsewhere. The label keeps the original name.
M270warningA journey task had a score outside 1 to 5, or a fraction, and Plotwork keeps whole scores from 1 to 5, so it was clamped or rounded.Check that the task still reads as you meant. 1 is frustrated and 5 is delighted.
M271warningA journey task named an empty actor, or the same actor in two spellings that differ only in case or spacing, so the empty name was dropped or the spellings were merged.Nothing, unless the two names were meant to be different people. Give them different words.
M260warningA timeline line had a # that is not part of an entity, and Mermaid reads everything after it as a comment, so the converter cut the line there as well.Check that the text before the # says what you meant. Write the # as the entity #35; in the Mermaid if it was part of the text.
M120errorA gitGraph statement names a branch or commit that does not exist, makes a branch that already exists, merges a branch into itself, or branches before the first commit, so the history cannot be replayed.Fix the line the diagnostic points at, or convert with lenient to skip statements that cannot be replayed.
M220warningMermaid draws a generated hash on a commit that has no id, and Plotwork draws a commit with no message as a bare dot.Give the commit an id: in the Mermaid if the picture should say something next to it.
M221warningA cherry-pick named a parent:, and Plotwork has no choice of parent: a cherry-pick copies one commit.Nothing, unless the picked commit is a merge and the parent mattered. Say which side in the commit message.
M222warningA commit had both an id: and a msg:, and Mermaid draws only the id, so the message was dropped.Put the text the picture should show in the id:, or in the message of the Plotwork commit.
M130errorA Gantt chart is scheduled in hours or minutes, or its dateFormat holds a time of day. Plotwork schedules in whole days, so nothing was converted.Redraw the plan in days, or keep the chart as Mermaid in its own mermaid fence.
M131errorA Gantt task comes after, or ends where, a task that is not in the chart, or waits for itself in a circle, so its dates cannot be worked out.Fix the id the diagnostic names, or give the task a start date. Convert with lenient to skip tasks that cannot be placed.
M132errorA Gantt dateFormat uses a token Plotwork does not read, such as a month name, so the dates cannot be read.Write the dates as YYYY-MM-DD, DD/MM/YYYY, or another format of a year, a month, and a day in numbers.
M230warningA Gantt axisFormat was dropped. Plotwork writes the labels of the axis itself, from the step it draws.Set unit= on the header to choose the step of the axis: day, week, month, quarter, or year.
M231warningA Gantt excludes or includes has no Plotwork form, apart from weekends and single days, which become weekends=skip and holidays=.Use weekends=skip and holidays= on the header, and check that the lengths still add up.
M232warningA Gantt tickInterval was dropped because it is not one day, week, or month, which Plotwork writes as unit=.Set unit= on the header, or leave it out so the step follows the span of the plan.
M233warningA Gantt todayMarker was dropped. Mermaid marks the day the chart is drawn, and Plotwork has no clock.Add today=YYYY-MM-DD to the header to mark a day, and move it as the plan does.
M234warningA Gantt milestone had a length, and Mermaid draws it in the middle of it, so it sits on one day in Plotwork.Check that the day is the one you mean, and set at= to change it.
M235warningA Gantt length changed: a fraction was rounded, a length of zero became one day, or a week was written in days because Mermaid counts it as seven working days.Check the length in the Plotwork source, and write it as a whole number of days, weeks, or months.
M236warningA Gantt task tag such as vert has no Plotwork form and was dropped. The tags done, active, crit, and milestone are kept.Use a milestone for a point in time, or today= for the current day.
M237warningA Gantt task ended where another task starts (until), and is written with the day that was, so it no longer moves with that task.Check the date. Use after= on the other task if it should follow this one instead.
M238warningA Gantt layout setting (topAxis, weekday, weekend, displayMode) was dropped. Plotwork chooses the layout.Nothing: the axis is above the chart, the week starts on Monday, and weekends are Saturday and Sunday.
M240warningA class member was written type first (String name, getName() String, List~int~ getIds()), and Plotwork writes a member name first, with the type after a colon, so it was turned round.Check that the member reads as you meant. When neither word reads as a type, or both do, the text is kept as it was.
M241warningA class relation has no exact Plotwork form (a lollipop interface, a dashed line with no marker, or a marker at both ends), so the nearest of the eight relations was drawn.Read the message for what was drawn. Edit the Plotwork if the nearest relation says the wrong thing.
M242warningA class had more than one annotation (<<interface>>, <<Service>>), and a Plotwork class has a type and one stereotype word, so the extra one was dropped.Keep the annotation that matters, or say the other in a note on the class.
M180errorA mind map has one root, and a later line is indented as far as it, or less, so it has no parent. Nothing was converted.Indent the line under the node it belongs to, or move it into its own mindmap block.
M280warningA mind map node shape has no exact Plotwork form, such as a bang or a cloud, so the nearest shape was drawn.Read the message for the shape that was drawn. Set shape= on the topic if another one reads better.
M281warningA mind map ::icon() line was dropped. Plotwork icons are a closed set.Set icon= on the topic. The hint names the closest bundled icon.
M282warningA mind map node had text before its brackets. Mermaid reads that as an id and does not draw it, so only the text inside the brackets was kept.Put all of the label inside the brackets if the text before them belongs in it.

Diagnostics from the converter point into the Mermaid. The Plotwork validator's own warnings, such as an icon suggestion, point into the converted source, and they come back separately in validation.

Before and after

Each example shows the Mermaid source, the picture Mermaid draws from it, the Plotwork the converter produced, the picture Plotwork draws from that, and a table of what differs, with nothing edited by hand. After the conversion, an agent would still read the diagnostics, choose types for the nodes left as step, and take the icon suggestions.

The Mermaid pictures were drawn with Mermaid 12.1.0, in its default and dark themes, with Arial text. They are fixed pictures, drawn once from the source above them. They are not drawn by this page.

Some differences hold for every diagram:

WhatMermaidPlotwork
MeaningThe shape of a node, and a style you write for each color.The type of a node, which sets its icon, tone, and shape. A color is never written.
Text for a reader who cannot see itA role and the kind of diagram. A title and a description only when the source has accTitle and accDescr lines.A title, a description, an outline of the nodes and connections, and the source the picture was drawn from, in every picture.
Text widthMeasured in a browser when the diagram is drawn, so one source can give boxes of different sizes on different machines.Measured with font metrics built into the renderer, so one source gives the same picture on every machine.
Light and darkA theme chosen when the diagram is drawn.A light picture and a dark picture of every diagram, chosen by the page.

A flowchart with a subgraph and a decision

Mermaid:

Mermaid
---title: Sign-in flow---flowchart TD  user[User] --> gw[API gateway]  gw --> auth{Credentials valid?}  auth -->|yes| session[Create session]  auth -->|no| denied([Access denied])  subgraph store [Data]    db[(Users DB)]    audit[Audit log]  end  auth -.-> db  session --> audit  classDef database fill:#9cf  class audit database

Mermaid draws it like this:

How Mermaid draws the sign-in flow: a title, boxes joined by arrows, a diamond for the decision, a rounded end, a cylinder for the database, and a filled box around the Data group.

The converter reports the database type it took from the class of that name for the audit log, the class's blue fill kept as the tone blue, and the one node left as a plain step. The Plotwork:

Sign-in flow: 7 nodes, 6 connections.

What differs between the two pictures:

WhatMermaidPlotwork
TitleDrawn above the diagram, from the front matter.Not drawn. It names the picture for a screen reader.
What a node isThe shape: a box, a diamond for the decision, a rounded end, a cylinder for the database. Every shape has the same lavender fill unless a style says otherwise.The type: an icon and a tone for each node, such as a person, a gateway, an amber decision, and a database. The end is a pill.
ColorclassDef database fill:#9cf paints Audit log blue, and the subgraph has a pale yellow fill.No color is written in the source. Audit log takes the type database from the class name, and the class's fill becomes tone=blue, which the theme draws in light and dark.
Edge labelsThe words yes and no on the lines.The same words, with a green tick and a red cross added, because those labels get a status icon.
The dotted linkA dotted line (-.->).A dashed line (-->), which is the line for a link that is optional or indirect.
The groupA filled box with its name centered at the top.A frame with a corner tab that holds the name.

A sequence diagram with an alt block

Mermaid:

Mermaid
---title: Token refresh---sequenceDiagram  autonumber  actor U as User  participant A as App  participant I as Identity provider  U->>A: Open the app  A->>+I: Refresh token  alt token valid    I-->>-A: New access token  else token revoked    I-->>A: 401 sign in again    A->>U: Ask to sign in  end  Note over A,I: Tokens last 15 minutes

Mermaid draws it like this:

How Mermaid draws the token refresh: a title, a stick figure and two boxes at the top and again at the bottom, numbered messages, an activation bar, a dotted alt frame, and a yellow note.

The converter turns autonumber into numbered=on on the header, reports the dropped activation marks, and reports the participant App left as a plain step. The actor became a person, and the name Identity provider became an identity. The Plotwork:

Token refresh: 3 participants.

What differs between the two pictures:

WhatMermaidPlotwork
ParticipantsA stick figure for the actor and a box for each participant, drawn at the top and again at the bottom.A card for each, at the top only, with an icon from its type: a person, and an identity provider. App stays a plain card, because its type is step.
Numbers and activationautonumber numbers each message, and activate draws a bar on the Identity provider.The numbers carry over as numbered=on, so each message gets the same numbered badge. The activation bar is dropped and reported.
The alt blockA dotted frame with an alt tab. The conditions sit in brackets at the center, over the lifeline of App.A rounded frame with an alt chip and an else chip, each beside its condition, at the left.
RepliesDashed lines with filled arrowheads.Dashed lines with open arrowheads.
The noteA yellow box.A tinted box with an accent bar.

An entity relationship diagram

Mermaid:

Mermaid
---title: Orders---erDiagram  CUSTOMER ||--o{ ORDER : places  ORDER ||--|{ LINE_ITEM : contains  CUSTOMER {    int id PK    string email UK    string name  }  ORDER {    int id PK    int customer_id FK    date placed_at  }  LINE_ITEM {    int order_id FK    string sku    int quantity  }

Mermaid draws it like this:

How Mermaid draws the orders: a title and three tables of type, name, and key columns, stacked in one column and joined by crow's foot lines with a label on each.

The many side of each relationship holds the foreign key, and the converter matched each one to a column that was already declared as FK, so it added none. It reports that the "one or more" minimum on contains is not drawn. The Plotwork:

Orders: 3 tables, 2 relations.

What differs between the two pictures:

WhatMermaidPlotwork
A tableThree columns: the type, the name, and the key.A card with an icon in its header. The key is a badge before the name, and the type sits at the right edge.
The unique keyWritten UK in the source and drawn UK.The converter turns UK into the flag unique, which is drawn as UQ.
The ends of a relationshipEach end says its minimum and its maximum: a double bar for exactly one, a circle and a crow's foot for zero or more.A tick for the one side and a crow's foot for the many side. The "one or more" minimum on contains is not drawn, and the converter reports that.
The labelIn a box on the line.In a pill on the line.
LayoutThe three tables stack in one column.The three tables sit in a row.

Converting a documentation folder safely

Go one file at a time, and read before you write. Ask your agent to run the steps, or make the calls yourself.

StepCallWhy
1. Dry runsync_markdown with the document, its path, convertMermaid: true, and mode: "check"Reports each Mermaid fence and changes nothing. Repeat for every Markdown file.
2. Read the reportEach fence has a status and its diagnostics. A failed fence could not be converted and is left exactly as it was.
3. Fix or acceptFences that fail stay as Mermaid. To convert a diagram that has a line that cannot be read, call convert_mermaid on it with lenient: true, which skips the line with a warning.
4. Writesync_markdown again, with mode: "sync"Replaces each convertible fence with a diagram fence and returns the picture files and blocks. Everything else in the file is unchanged, byte for byte. Save the markdown over the file and each entry of files at its path.
5. Reviewgit diffRead every changed diagram before it is committed.

To convert a whole folder, ask the agent to call sync_markdown on each file. Keep the dry run first for every file.

For a single diagram, call convert_mermaid with the Mermaid text. It returns the Plotwork source and the diagnostics as data, and you decide where to save the source.

FAQ

QuestionAnswer
Does it handle my theme and colours?Only as meaning. A class or style fill becomes the nearest tone, reported with M204, and the theme draws that tone in light and dark. Themes, strokes, text colours, and a fill with no tone are dropped with a diagnostic.
What about links and click actions?Dropped with a diagnostic. A diagram is a static picture. Link to the page in the text around it.
Can I convert a whole repository?Yes. Ask the agent to call sync_markdown on each Markdown file. Dry run first.
How do I review the result?Read the diagnostics, then the outline (outline in the result), then the picture, then the git diff.
Why is a node a plain step?Nothing in the Mermaid says what it is. Choose a type: database, service, queue, person, gateway, function, external.
Why did an edge move?Plotwork edges connect nodes, so an edge into a subgraph goes to a node inside it. A diagnostic names the node.
What if one line cannot be read?By default nothing is converted, so a diagram never loses a line unnoticed. The error names the line. Set lenient: true on convert_mermaid to skip such lines with a warning and convert the rest, then add what was skipped by hand.
Can I keep some diagrams as Mermaid?Yes. A fence that is not converted stays as it was, and GitHub keeps drawing it.
Does the converter call a model or the network?No. It is plain code. It does not fetch URLs, read files, or run anything from the diagram.
What are the size limits?50,000 characters, 300 nodes, 600 edges, and 24 levels of nesting for one diagram. Larger input comes back as an error that says to split it.

See also

DocWhy
Writing diagramsThe Plotwork language by example
Writing diagrams with an agentThe validate and fix loop, and the one-call workflows
Diagrams in Markdown and on GitHubHow a fence becomes a picture
Tool referenceconvert_mermaid, sync_markdown, and the other tools
Language referenceEvery word the language accepts, and every diagnostic code