@erclx/canon 5.24.0 → 5.25.0

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.
Files changed (80) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/backlog-triage/REQUIREMENT.md +2 -2
  3. package/claude/skills/backlog-triage/SKILL.md +19 -6
  4. package/claude/skills/canvas/REQUIREMENT.md +49 -0
  5. package/claude/skills/canvas/SKILL.md +106 -0
  6. package/claude/skills/design-extract/REQUIREMENT.md +1 -1
  7. package/claude/skills/draft-doc/REQUIREMENT.md +1 -1
  8. package/claude/skills/draft-figure/REQUIREMENT.md +14 -3
  9. package/claude/skills/draft-figure/SKILL.md +24 -51
  10. package/claude/skills/draft-figure/references/architecture-set.md +35 -0
  11. package/claude/skills/draft-figure/references/hand-drawn.md +31 -0
  12. package/claude/skills/draft-figure/references/mermaid.md +55 -0
  13. package/claude/skills/git-followup/SKILL.md +17 -6
  14. package/claude/skills/git-pr/references/evidence.md +4 -2
  15. package/claude/skills/git-split/SKILL.md +1 -1
  16. package/claude/skills/index-lookup/SKILL.md +2 -2
  17. package/claude/skills/review-address/SKILL.md +6 -13
  18. package/claude/skills/review-address/references/rest-reads.md +43 -0
  19. package/claude/skills/review-craft/references/ui.md +7 -1
  20. package/claude/skills/review-pr/REQUIREMENT.md +1 -0
  21. package/claude/skills/review-pr/SKILL.md +9 -3
  22. package/claude/skills/review-pr/references/close-out.md +2 -2
  23. package/claude/skills/review-pr/references/post.md +17 -5
  24. package/claude/skills/review-pr/references/scope.md +10 -8
  25. package/claude/skills/review-ui/SKILL.md +12 -4
  26. package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +30 -1
  27. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +4 -0
  28. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +4 -4
  29. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +6 -0
  30. package/claude/skills/role-orchestrator/scripts/poll.ts +90 -27
  31. package/claude/skills/role-orchestrator/scripts/repo.ts +3 -5
  32. package/claude/skills/role-orchestrator/scripts/watch.ts +4 -7
  33. package/docs/agents/canvas.md +1 -1
  34. package/docs/agents/commands.md +45 -45
  35. package/docs/agents/context-audit-checks.md +2 -2
  36. package/docs/agents/index.md +0 -1
  37. package/docs/agents/key-changes.md +3 -3
  38. package/docs/agents/pr-evidence.md +11 -2
  39. package/docs/agents/pr-reads.md +5 -4
  40. package/docs/target/projects.md +4 -4
  41. package/docs/workflow/ai-workflow.md +0 -1
  42. package/docs/workflow/skill-map.md +3 -3
  43. package/docs/workflow/visual-design-workflow.md +1 -0
  44. package/governance/renames.toml +0 -4
  45. package/governance/rules/standards/markdown.md +1 -0
  46. package/package.json +1 -2
  47. package/scripts/core/install-check.sh +1 -1
  48. package/src/audits/catalog.ts +2 -2
  49. package/src/canvas/client/inspector/field.tsx +200 -0
  50. package/src/canvas/client/inspector/fields.css +151 -0
  51. package/src/canvas/client/inspector/section.tsx +27 -0
  52. package/src/canvas/client/inspector/values.ts +60 -0
  53. package/src/canvas/client/inspector.tsx +154 -115
  54. package/src/canvas/client/shell.css +0 -101
  55. package/src/claude/cases/misc.ts +5 -0
  56. package/src/claude/cases/workflow.ts +1 -1
  57. package/src/claude/seeds.ts +0 -1
  58. package/src/commands/design.ts +2 -64
  59. package/src/commands/pr.ts +219 -202
  60. package/src/context/audit.ts +7 -6
  61. package/src/help.ts +2 -2
  62. package/src/migrate/skill-names.ts +7 -1
  63. package/src/pr/evidence.ts +25 -5
  64. package/src/pr/rest.ts +137 -0
  65. package/standards/canvas.md +112 -0
  66. package/standards/context.md +2 -1
  67. package/standards/figures.md +0 -1
  68. package/standards/index.md +1 -1
  69. package/standards/markdown.md +1 -0
  70. package/standards/mermaid.md +1 -7
  71. package/tooling/claude/reference.md +0 -5
  72. package/claude/skills/draft-diagram/REQUIREMENT.md +0 -45
  73. package/claude/skills/draft-diagram/SKILL.md +0 -177
  74. package/docs/agents/design-board.md +0 -33
  75. package/governance/rules/standards/diagrams.md +0 -14
  76. package/governance/rules/standards/mermaid.md +0 -15
  77. package/scripts/core/check-gallery-exclusion.sh +0 -50
  78. package/src/design/board.ts +0 -576
  79. package/standards/diagrams.md +0 -132
  80. package/tooling/claude/seeds/.claude/diagrams/index.md +0 -8
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
4
- "version": "5.24.0",
4
+ "version": "5.25.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -9,12 +9,12 @@ description: Why the backlog gets a measured verdict per row with a lean toward
9
9
 
10
10
  Without this skill, the backlog only grows. `task-board` declines or archives one named task, and nothing reads the backlog as a whole, so a row stays until somebody happens to remember it and decides by hand. The orchestrator's backlog re-test only clears a row upward onto the board, so no sweep ever takes one off. A backlog past fifty rows had no surface that could say which of them still mattered.
11
11
 
12
- Three failure modes cost more than the rest. A pass that judges a row by what its task file says rather than by the tree keeps every row, since a task file reads as live the day it was written and every day after. A pass that applies its own verdicts decides rows on silence, which ships a decline nobody approved on a board with no history to restore it from. And a keep that carries no measurement is the default nobody argues with, so a triage without a lean toward decline ends with the same backlog it started with.
12
+ Four failure modes cost more than the rest. A pass that judges a row by what its task file says rather than by the tree keeps every row, since a task file reads as live the day it was written and every day after. A pass that applies its own verdicts decides rows on silence, which ships a decline nobody approved on a board with no history to restore it from. A keep that carries no measurement is the default nobody argues with, so a triage without a lean toward decline ends with the same backlog it started with. And a keep that measures only that the defect still exists keeps nearly everything, since a row filed against the tree almost always still reproduces. One pass suggested keeping 24 of 27 rows that way, where the operator kept 6 and promoted 1.
13
13
 
14
14
  ## Must
15
15
 
16
16
  - Measure every row against the tree and the log during the pass, carrying a count, a path, or a commit rather than the task file's own claim
17
- - Suggest decline for any row this pass cannot argue for, putting the burden of proof on keep and promote
17
+ - Suggest keep or promote only for a row that names a cost, being a wrong result the pass observed or a case someone hit, including a user asking for a feature or working around its absence, or a reach, being a defect on a path the project ships that changes what its users or targets receive, hit or not, and suggest decline for a row whose defect merely still exists
18
18
  - Name the commit or pull request per outcome position before suggesting archive
19
19
  - File into an ordinary intake folder, so the intake list and answer verbs read and write it unchanged
20
20
  - Carry the verdict in the suggestion line and the task stem in the item heading, rather than in new fields
@@ -45,18 +45,31 @@ A row whose link resolves to no file gets an item anyway, carrying `decline` and
45
45
 
46
46
  Measure every claim the task makes against the tree and `git log` during this pass. Grep for the construct its defect names and count the sites, check whether each open outcome's behavior exists, and search the log for a commit or pull request that shipped it under another task. Never carry a figure from the task file forward as current, since the task states what was true when it was filed.
47
47
 
48
+ Then gather the evidence Step 5 reads for each row that reproduces. Check whether its origin line or `## Findings` names an issue, a run, or a person it bit. Search the log for a commit or pull request that worked around it rather than fixing it. For a defect in something the project ships, whether a route or a page its users call or a rule, a seed, a scaffold, or a verb other repositories copy or run, name the shipped path it sits on. Record each one that is absent as absent rather than inferring it.
49
+
48
50
  Name the commit the pass measured against in the overview body.
49
51
 
50
52
  ### Step 5: pick the verdict
51
53
 
52
- Keep and promote carry the burden of proof. A row nobody can argue for from this pass's measurement is declined.
54
+ Keep and promote carry the burden of proof, and a defect that still exists does not meet it. A row filed against the tree nearly always still reproduces, so a test of reproduction alone keeps almost every row.
55
+
56
+ A row earns keep or promote only by naming one of two things from the evidence Step 4 gathered:
57
+
58
+ - **A cost**: the defect gives a wrong result that this pass observed, by running the verb that carries it or reading the committed output that shows it, or that the row's evidence shows an issue, a run, or a person hit, a workaround commit included. A hit counts only while the pass does not measure it as fixed or as no longer recurring.
59
+ - **A reach**: the defect sits on a path the project ships, and everyone who reaches that path, whether a user calling it or a target copying or running it, receives the defect as a wrong instruction, a broken, failing, or unbounded behavior, or a wrong output. Reach needs no hit, since the path's users meet the defect whether or not anyone reported it.
60
+
61
+ A plausible cost names nothing. A row arguing what could go wrong off a shipped path, with no observed output and no hit, is declined however likely the argument sounds. Three things are not a reach: a missing feature on a shipped surface, a defect that changes nothing a user or a target receives, such as dead code on a shipped path, and a defect that shows only in this repository's own config, workflows, scripts, or fixtures.
62
+
63
+ A missing protection is a defect, not a missing feature. Read a row as a missing protection when the shipped path lacks a bound, a check, or a guard on what it does at run time, such as a limit, a timeout, or input validation, so that the path can fail, run unbounded, or return a wrong output for every user who reaches it. That is a reach. A missing feature leaves the path working as it does today and adds a capability, a document kind, or a sentence of guidance, and it is not a reach.
64
+
65
+ A missing feature can still earn keep or promote through the cost arm. A user asking for it in an issue or a report, or working around its absence on their side, is a case someone hit, so that row is never declined for being a feature. A feature nobody asked for is declined. A passing mention is not a request, and neither is one the project's own planning records filed with no user behind it.
53
66
 
54
- - **keep**: the defect or gap reproduces this pass, stated with a count or a path.
55
- - **promote**: it reproduces, and the thing it was waiting on has cleared, naming what cleared it.
67
+ - **keep**: the defect reproduces this pass, stated with a count or a path, and the row names a cost or a reach, stated with the observation, the hit, or the shipped path.
68
+ - **promote**: it qualifies for keep, and the thing it was waiting on has cleared, naming what cleared it.
56
69
  - **archive**: every open outcome is measured shipped, naming the commit or pull request per outcome position.
57
- - **decline**: anything else, including a defect the tree no longer holds, work superseded by another task, and a row whose argument rests only on its original filing.
70
+ - **decline**: anything else, including a defect that reproduces but names no cost and no reach, a defect the tree no longer holds, work superseded by another task, and a row whose argument rests only on its original filing.
58
71
 
59
- Age is evidence in neither direction. A row untouched for months is not more declinable than one filed yesterday, and a row filed yesterday is not more worth keeping.
72
+ Age is evidence in neither direction. A row untouched for months is not more declinable than one filed yesterday, and a row filed yesterday is not more worth keeping. An old hit is read the same way as a new one, by whether the pass measures it as still able to recur.
60
73
 
61
74
  ### Step 6: write the items
62
75
 
@@ -64,7 +77,7 @@ Write one item per row in the intake item format, one cluster file per domain th
64
77
 
65
78
  - `Problem:` states what this pass measured, with the count, path, or commit behind it
66
79
  - `Fix:` states what applying the verdict does to the board
67
- - `Worth it:` carries the verdict's reason in one clause
80
+ - `Worth it:` names the cost or the reach the verdict rests on in one clause, or says the row names neither. A `yes` or a `later` carries no verdict on its own, so a decline can follow either word
68
81
  - `Open:` reads `decline, archive, keep, or promote?` on every item
69
82
  - `Suggested:` opens with exactly one of the four verdict tokens, then the reason. An archive suggestion lists every outcome position it closes, as `archive 1, 3: shipped in #NNN`
70
83
  - `Overlaps:` names a live board task that already carries the work, where one does
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: canvas
3
+ description: Scope boundary for driving the local design canvas, and the line between a drafted frame and the design it feeds
4
+ ---
5
+
6
+ # Canvas requirement
7
+
8
+ ## Gap
9
+
10
+ Without this skill, a session asked to draw on the canvas writes frames into a folder nothing is serving, so the operator sees nothing and the session reports the frames as drawn. When the server is running, the session trusts a passing capture as proof the canvas works. Captures go through the frame route, so a shell that answers its address with an empty page passes every capture while the operator looks at a blank tab.
11
+
12
+ It reads "this one" from the conversation rather than from the operator's selection, and when it does read the selection it acts on an element index recorded against an earlier version of the file, which lands the edit on a different element. It restyles through a class while the operator's inspector edit sits on the element as inline style, so the change never shows and the session reports it as made.
13
+
14
+ It writes frames from a linked worktree with the file tools, which refuse the main-root path and offer a worktree copy the canvas never serves. It captures after every edit, filling session scratch with images of drafts nobody picked.
15
+
16
+ When the operator picks a frame, the session leaves the decision on the canvas and cites the frame as the design, so the project's design document never learns it and a gitignored file becomes the only record.
17
+
18
+ ## Must
19
+
20
+ - Start the canvas when it is not serving, before writing any frame
21
+ - Confirm the live shell loads at the address the server reported, rather than trusting a capture
22
+ - Read the address off the server's record rather than composing one
23
+ - Make pages and frames through the verbs, so a box and the operator's drags share one locked writer
24
+ - Name the main-root write route for a frame written from a linked worktree
25
+ - Read the operator's selection when they point at something, and treat a stale element pick as unknown
26
+ - Read a frame's inline styles before restyling it, and say which operator edits were kept and which dropped
27
+ - Capture once per finished frame or page rather than after every edit
28
+ - Carry a picked direction into the project's design document or wireframes
29
+
30
+ ## Must not
31
+
32
+ - Present the canvas as the project's source of truth for design
33
+ - Promise the canvas survives a lost checkout
34
+ - Name any third-party design tool, or the canvas's predecessor surface
35
+ - Be invoked only by the author typing its name. A request to draw, select, or capture on the canvas should reach it by description, and a run where it never fires on such a request is a defect in the description
36
+
37
+ ## Guards
38
+
39
+ - No `canon` on PATH, or an installed CLI with no canvas command, stop rather than writing files nothing serves
40
+ - A server that refuses to start, or a shell that answers blank, stop and report it rather than drawing into it
41
+
42
+ ## Out of scope
43
+
44
+ - What the canvas verbs do internally, which is the CLI's own contract
45
+ - The content format a frame and a layout take, which the canvas standard fixes
46
+ - Drafting candidates for a taste decision and taking the pick, which `draft-and-pick` runs on its own render path
47
+ - Tracing reference images into design values, which is `sketch-design`
48
+ - Writing the design document itself, which is `design-extract`
49
+ - Backing up the canvas folder off one disk
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: canvas
3
+ description: Drives the local design canvas, where pages hold HTML frames the operator sees, drags, selects, and edits in the browser. Starts the canvas when it is not running, confirms the live shell loads, makes pages and frames, edits a frame through its file, reads what the operator selected or restyled, captures finished frames, and carries a picked direction into the project's design document. Use when asked to "open the canvas", "put this on the canvas", "draw a frame for X", "change the one I selected", "what did I pick", "capture this frame", or "move that frame on the canvas". Do NOT use to trace reference images into design values, which is `sketch-design`, or to write the design document from code, which is `design-extract`.
4
+ ---
5
+
6
+ # Canvas
7
+
8
+ Drive the canvas the operator is looking at. A page is a folder of HTML frames and a frame is one file, so a session draws by writing files and the operator sees each write land in the browser. The content format is fixed by `${CLAUDE_SKILL_DIR}/../../standards/canvas.md`. Read it before writing a frame.
9
+
10
+ Every verb below is `canon canvas`. Run `canon docs canvas` for the full verb reference, its records, and its refusals.
11
+
12
+ ## Guards
13
+
14
+ - If `canon` is not on PATH, stop: `❌ canon CLI not found.`
15
+ - If `canon canvas --help` reports no such command, stop: `❌ The installed canon carries no canvas command. Update the CLI and re-run.`
16
+ - Write no frame before the canvas is serving and the shell has loaded. A frame nobody can see is a draft reported as delivered.
17
+ - Never present a frame as the project's design. The canvas is a drafting surface and the project's design document or wireframes hold what was decided.
18
+ - Never promise the canvas survives a lost checkout. Its folder is gitignored and nothing backs it up.
19
+
20
+ ## Step 1: start the canvas and confirm the shell
21
+
22
+ Read the pages first, which also proves the verb resolves:
23
+
24
+ ```bash
25
+ canon canvas list --json
26
+ ```
27
+
28
+ The record carries `content`, the folder every page lives in. It sits at the main worktree root whichever worktree the session runs in, so write through that path rather than one composed from the current directory.
29
+
30
+ Start the server in the background unless the operator named an address that already answers, since it runs until interrupted. No verb reports a running server, and a second one is harmless, since it walks to the next free port and serves the same folder:
31
+
32
+ ```bash
33
+ canon canvas serve --json
34
+ ```
35
+
36
+ Read `url` off the record rather than composing one, since the server walks past a port already in use. Report the refusal and its `reason` when `ok` is false, and stop there.
37
+
38
+ Then fetch the printed address and check the shell itself, not a capture:
39
+
40
+ - The body is non-empty and carries a `<script` tag. An empty `200` means the shell failed to build, usually a dependency missing from the canvas tool's install, and every capture still passes because captures go through the frame route alone. Report it as a blank shell and stop.
41
+ - `<url>/api/pages` answers with JSON listing the pages.
42
+
43
+ Hand the operator the address as a markdown link carrying the URL as its text and its target.
44
+
45
+ ## Step 2: make pages and frames
46
+
47
+ - Add a page for each decision being drafted: `canon canvas page add <page> --json`.
48
+ - Add each frame through the verb, which writes the starting file and its box in one locked step: `canon canvas frame add <page> <frame> --width <px> --height <px> --json`. Give the width the frame should render at, since a capture and the canvas both render at it.
49
+ - Lay frames out with `canon canvas frame move <page> <frame> --x <px> --y <px> --json` rather than writing `layout.json` by hand, since the operator's drags write the same file under a lock.
50
+ - Name pages and frames for what they hold, such as `pricing` and `pricing-dense`, so the operator can say which one by name.
51
+
52
+ ## Step 3: edit a frame through its file
53
+
54
+ Write the frame's whole document to the `path` the add verb reported. The open canvas reloads the frame when its file changes.
55
+
56
+ - From a linked worktree, `Edit` and `Write` refuse a main-root path. Write the whole file through one plain `Bash` heredoc instead, the route `session-worktree` states for a main-root write, and never take the redirect to a worktree copy.
57
+ - Change one property of one element with `canon canvas edit <page>/<frame> --element <index> --set <property>=<value> --json` when the set it accepts covers the change, since it leaves every other byte as it was.
58
+ - Use the project's custom properties for every value the token stylesheet defines. The record from `serve` names the token source under `tokens`, and a `none` source means frames render unstyled, so say so rather than inventing values to cover it.
59
+
60
+ ## Step 4: read what the operator pointed at
61
+
62
+ Read the selection when the operator says "this one", "the selected frame", or "that button":
63
+
64
+ ```bash
65
+ canon canvas selection --json
66
+ ```
67
+
68
+ - `selection: null` means nothing is selected or the selected frame is gone. Ask which one rather than guessing.
69
+ - An `element` carrying `stale: true` was picked against an earlier version of the file, so its `index` may now land on a different element. Treat the pick as unknown and ask the operator to pick again, rather than acting on the index or matching its `tag` and `text` by eye.
70
+ - A fresh `element` gives the `index` that `canvas edit` takes.
71
+
72
+ ## Step 5: respect the operator's inspector edits
73
+
74
+ The operator restyles elements from the inspector, and each edit lands as inline `style` on the element. Inline style beats a class, so a class change made afterwards does not show.
75
+
76
+ - Read the frame's inline `style` attributes before restyling anything in it.
77
+ - Keep an inline property the operator set unless the change replaces it, and drop it, through `canvas edit` with an empty value or by rewriting the file, when it does.
78
+ - Say which operator edits were kept and which were dropped.
79
+
80
+ ## Step 6: capture finished work
81
+
82
+ - Capture a frame once it is finished: `canon canvas capture <page>/<frame> --json`. Each PNG is the frame's whole document, so its height follows the content rather than the box.
83
+ - Capture a whole page once its frames are finished: `canon canvas capture <page> --composite --json`. The image shows each frame clipped to its box at its layout position.
84
+ - Capture once per finished frame or page, not after every edit. The live canvas is the working view, and a capture is the record handed on.
85
+ - Read each `path` off the record rather than composing one.
86
+ - A capture refused because a font is not installed names the family it read off the frame's `html` element. Set that element's `font-family` to a family the machine has and capture again, rather than reporting the frame as captured.
87
+
88
+ ## Step 7: carry a picked direction back
89
+
90
+ When the operator picks a frame, the decision leaves the canvas.
91
+
92
+ - Carry visual values, being color, type, spacing, and radius, into the project's design document, through the skill that owns it. Carry structure and layout into the project's wireframes.
93
+ - Name the frame the decision came from in the reply, and leave the frame on the canvas until the operator clears it.
94
+ - Never cite a frame path from a tracked file. The folder is gitignored, so the citation resolves on this machine alone.
95
+
96
+ ## Output
97
+
98
+ ```plaintext
99
+ ✅ Canvas: <url the serve verb reported>
100
+ Page: <page>, <n> frames
101
+ Selected: <page>/<frame>, element <index>, or none, or stale
102
+ Capture: <path of each capture written>
103
+ Carried: <the design document or wireframe the pick went to, or not yet>
104
+ ```
105
+
106
+ Write the canvas line's URL as a markdown link. Omit the capture line when nothing was captured and the carried line when no direction was picked. Emit every path from the project root.
@@ -39,4 +39,4 @@ The greenfield case is the one that fails quietly. A project with no UI code sti
39
39
  - Mutating an existing design system, which is a direct edit of the file rather than a skill
40
40
  - Producing the HTML and CSS preview, which `canon design render` owns
41
41
  - Auditing the implemented UI against the tokens, which `ux-audit` owns
42
- - Architecture and flow diagrams, which `draft-diagram` owns
42
+ - Architecture and flow diagrams, which `draft-figure` owns
@@ -42,5 +42,5 @@ The same procedure ran as five skills, one per kind, each restating the read, th
42
42
  - Rewriting or syncing an existing `docs/` page or an authored README against a diff: `docs-sync`
43
43
  - Refreshing an existing context entry or wireframe, or stubbing a surface a diff touched: `context-fold`
44
44
  - Drafting a standard or a governance rule: `create-standard`, `create-rule`
45
- - Drawing a figure or a diagram for a document: `draft-figure`, `draft-diagram`
45
+ - Drawing a figure or a diagram for a document: `draft-figure`
46
46
  - Voice, rhythm, punctuation, and formatting in the drafted document: the `write-human` skill and the markdown standard
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: draft-figure
3
- description: Why a figure decides between Mermaid and freehand SVG before drawing, and why only the Mermaid path carries a render-verification loop
3
+ description: Why a figure decides between Mermaid, an architecture view, and freehand SVG before drawing, and why the Mermaid path carries a render-verification loop
4
4
  ---
5
5
 
6
6
  # Draft figure requirement
@@ -11,18 +11,24 @@ Without this skill, a session asked for a figure reaches for whichever path it t
11
11
 
12
12
  A teach lesson breaks the Mermaid path outright. The lesson letters its figures in a hand font the renderer cannot load, so the render sizes every label in a narrower fallback face and the lesson clips the text it actually shows. The check then reads the standalone image, rendered in that same fallback, and passes a figure the reader sees clipped. Both happened on one lesson, where every group title clipped mid-word and only a capture taken inside the lesson showed it.
13
13
 
14
+ An architecture view asked for one at a time fails its own way. A session asked to refresh one view redraws every view beside it, so pictures nobody checked change in the same commit and the reviewer cannot tell which one the pass was about. It reaches for C4, state, or class diagrams that render differently in every viewer. A Mermaid source can satisfy every rule in the standard and still render as a picture asserting something false about the system, so a session that judges the source and calls it verified has spent the only signal a reader has on a picture nobody looked at.
15
+
14
16
  The color failure is the quiet one. Mermaid's own theme writes literal hex values, and a figure carrying one stops re-coloring itself the moment the host page switches its palette, which the figure standard's own accessibility bar rules out.
15
17
 
16
18
  ## Must
17
19
 
18
20
  - Apply the standard's earn-its-place bar before drawing anything
19
21
  - Decide between the Mermaid path and the freehand path by whether the subject reduces to a graph, not by which is faster to produce
22
+ - Draw an architecture view from its named source signal, inside `flowchart` and `sequenceDiagram`, and render it through the Mermaid path
23
+ - Draw only the view the request names, leaving every other figure in the destination untouched
20
24
  - Draw a figure bound for a teach lesson freehand whatever its shape, since the lesson's hand font is one the renderer cannot measure
21
25
  - Verify a figure bound for a rendered page by capturing it inside that page, and fall back to the standalone render only where the destination has no page to load
22
26
  - Keep the scoped overflow style as the documented fallback for a clipped group title wherever the Mermaid path still runs
23
27
  - Render the Mermaid path with the hand-drawn look and the figure font stack, never with Mermaid's default look
24
28
  - Rewrite every literal color the renderer wrote into the custom property the host stylesheet defines
25
- - Read the Mermaid render back and judge it against what the figure means to say, the same verification `draft-diagram` runs for the architecture surface
29
+ - Read the Mermaid render back and judge it against what the figure means to say, rather than judging the source
30
+ - Read the node labels by hand, since no hook checks inside a fence
31
+ - Cite only code paths that exist in an architecture view's caption or prose
26
32
  - Wrap either path in a `<figure>` and `<figcaption>` and give it an accessible name
27
33
 
28
34
  ## Must not
@@ -33,16 +39,21 @@ The color failure is the quiet one. Mermaid's own theme writes literal hex value
33
39
  - Report a clean verification when the Mermaid render was skipped or a defect survived
34
40
  - Loop past two correction passes on a surviving defect
35
41
  - Commit a scratch render. Only the SVG that ships in the destination document survives
42
+ - Redraw a figure the request did not name, or report one as drawn when it was left alone
43
+ - Leave an architecture view as an unrendered fence
36
44
 
37
45
  ## Guards
38
46
 
39
47
  - No subject given: stop, since nothing states what the figure has to show
40
48
  - No destination document given: stop, since the figure has nowhere to land
41
49
  - Subject does not clear the earn-its-place bar: stop rather than drawing decoration
50
+ - An architecture view with no planning file and no folder structure to scan: stop, because nothing anchors it
51
+ - Render fails for any reason: continue to the output and name the skipped check. A missing renderer degrades the loop rather than failing it.
42
52
 
43
53
  ## Out of scope
44
54
 
45
- - The toolkit's own `.canon/diagrams/` architecture surface, which shares no code, font, or visual language with this convention: `draft-diagram`
46
55
  - Mermaid's own layout, budgets, and label rules for the fence itself: the mermaid standard, cited rather than restated here
47
56
  - A UI wireframe or screen mockup: `draft-doc`
57
+ - Design tokens and the visual system: `design-extract`
58
+ - Implementation detail behind an architecture view, which belongs in a context entry the caption points at
48
59
  - Which lesson needs a figure and where it sits among the lesson's blocks: `teach-workspace`, which calls this skill to draw one
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: draft-figure
3
- description: Drafts one hand-drawn figure for a document, deciding whether the subject is Mermaid-shaped or needs freehand SVG, then draws it. Renders the Mermaid path through Mermaid's own hand-drawn look, reads the render back, and fixes what it exposes. Wraps either path in the figure standard's caption, color, and accessibility rules. Use when asked to "draw a figure", "add a hand-drawn diagram", "draft a figure for this doc", or "draw this relationship as a figure". Do NOT use for the toolkit's own `.canon/diagrams/` architecture surface, which is `draft-diagram`, or for a UI wireframe, which is `draft-doc`.
3
+ description: Drafts one hand-drawn figure for a document, deciding whether the subject is Mermaid-shaped, an architecture view, or needs freehand SVG, then draws it. Renders the Mermaid path through Mermaid's own hand-drawn look, reads the render back, and fixes what it exposes. Wraps every path in the figure standard's caption, color, and accessibility rules. Use when asked to "draw a figure", "add a hand-drawn diagram", "draft a figure for this doc", "draw this relationship as a figure", "draw the architecture", "diagram the system", or "show the components". Do NOT use for a UI wireframe, which is `draft-doc`, or for design tokens, which is `design-extract`.
4
4
  ---
5
5
 
6
6
  # Draft figure
7
7
 
8
- Decide whether a requested figure reduces to a graph Mermaid can draw, or needs freehand SVG, then draw it and verify what a reader will actually see.
8
+ Decide whether a requested figure reduces to a graph Mermaid can draw, is one of the architecture views, or needs freehand SVG, then draw it and verify what a reader will actually see.
9
9
 
10
10
  ## Guards
11
11
 
@@ -17,7 +17,6 @@ Decide whether a requested figure reduces to a graph Mermaid can draw, or needs
17
17
  Read these in parallel:
18
18
 
19
19
  - `${CLAUDE_SKILL_DIR}/../../standards/figures.md`: when a figure earns its place, the render-first policy, wrapping and captioning, and the color and accessibility rules
20
- - `${CLAUDE_SKILL_DIR}/../../standards/mermaid.md`: direction and layout, node and edge budgets, the accessibility fields, and how a render is verified, for the Mermaid path
21
20
  - `${CLAUDE_SKILL_DIR}/../../standards/markdown.md`: formatting for the caption prose
22
21
  - The `write-human` skill: voice and rhythm for the caption prose
23
22
 
@@ -25,74 +24,48 @@ Read these in parallel:
25
24
 
26
25
  Apply the standard's bar: a relationship, a boundary, a path, or a before-and-after that prose or a list cannot carry as well. Stop when it does not: `❌ <subject> reads fine as prose. A figure adds nothing here.`
27
26
 
28
- ## Step 3: decide the render path
27
+ ## Step 3: pick the path
29
28
 
30
- - **A teach lesson**, meaning the destination is a lesson page in a learning workspace's `lessons/` folder: draw it through Step 5 whatever its shape. The lesson letters its figures in a hand font the renderer cannot load, so a Mermaid render measures its labels in a narrower face and the lesson clips them.
31
- - **Graph-shaped**, meaning the relationship, boundary, or path is already what a flowchart or sequence diagram expresses: draw it through Step 4.
32
- - **Not graph-shaped**, meaning the subject is a spatial or physical arrangement no flowchart reduces to: draw it through Step 5.
29
+ Take the first that matches, and state which path was picked and why in one sentence:
33
30
 
34
- Take the first that matches, and state which path was picked and why in one sentence. The standard states this as a render-first policy rather than a ban on any tool-exported drawing, so outside a teach lesson a graph-shaped subject always routes through Mermaid even though that is itself an external renderer.
31
+ 1. **A teach lesson**, meaning the destination is a lesson page in a learning workspace's `lessons/` folder: the hand-drawn path, whatever the subject's shape, an architecture view included. The lesson letters its figures in a hand font the renderer cannot load, so a Mermaid render measures its labels in a narrower face and the lesson clips them.
32
+ 2. **An architecture view**, meaning the request names system context, components, request flow, data pipeline, or deployment: the architecture set, then the Mermaid path.
33
+ 3. **Graph-shaped**, meaning the relationship, boundary, or path is already what a flowchart or sequence diagram expresses: the Mermaid path.
34
+ 4. **Anything else**, a spatial or physical arrangement no flowchart reduces to: the hand-drawn path.
35
35
 
36
- ## Step 4: the Mermaid path
36
+ The standard states this as a render-first policy rather than a ban on any tool-exported drawing, so outside a teach lesson a graph-shaped subject always routes through Mermaid even though that is itself an external renderer.
37
37
 
38
- - Draft the fence against `mermaid.md`: direction, node and edge budgets, `accTitle`/`accDescr`, and quoted labels.
39
- - Render it with the hand-drawn look and the figure font stack, writing scratch files under `.canon/tmp/figures/`:
38
+ ## Step 4: draw
40
39
 
41
- ```bash
42
- mkdir -p .canon/tmp/figures
43
- cat <<'EOF' >.canon/tmp/figures/<slug>.json
44
- {"look": "handDrawn", "themeVariables": {"fontFamily": "Virgil, Excalifont, cursive"}}
45
- EOF
46
- bunx -y @mermaid-js/mermaid-cli -i .canon/tmp/figures/<slug>.mmd -o .canon/tmp/figures/<slug>.svg -c .canon/tmp/figures/<slug>.json
47
- bunx -y @mermaid-js/mermaid-cli -i .canon/tmp/figures/<slug>.mmd -o .canon/tmp/figures/<slug>.png -c .canon/tmp/figures/<slug>.json
48
- ```
49
-
50
- Render both formats in one pass: the SVG is what ships inside the `<figure>`, and the PNG is what Step 6 reads back, since an SVG's markup carries no recoverable spatial meaning the way `draft-diagram` Step 5 already states for the architecture surface.
51
-
52
- Use `bunx` when bun is available, falling back to `npx -y @mermaid-js/mermaid-cli ...` otherwise.
53
-
54
- - Rewrite every stroke, fill, and text color the renderer wrote as a literal hex value into the custom property the host stylesheet defines, per the standard's color rule. Mermaid's own theme has no notion of a custom property, so this is a source edit made to the rendered SVG, not a config option.
55
- - Expect a group title or a long label to clip once the destination shows it in the hand font, since the renderer sized its box in a fallback face.
56
- - When the in-page check shows a clipped title, add a `<style>` inside the SVG, scoped to that SVG's own id, that sets `overflow: visible` on the clipped label's `foreignObject` and centers the label within it, so the wider text spills past both edges evenly. The style hides the mismatch for a title and does nothing for a node label crowding its box, which takes a shorter label instead.
57
- - Check whether the project ran `canon design css --figures`. The figure and figcaption styling ships unconditionally either way, but that flag alone embeds the Virgil and Excalifont font files. Without it, the figure falls back to the browser's own generic `cursive` face rather than the intended hand-drawn font, and Step 7's output names that gap when the flag was not passed.
58
-
59
- ## Step 5: the freehand path
40
+ Read the reference for the path Step 3 picked and follow it:
60
41
 
61
- - Author the SVG by hand: plain shapes and lines authored directly in the markup.
62
- - Follow the standard's `## Color and accessibility` section for every fill, stroke, and accessible name.
42
+ - Architecture set: read `${CLAUDE_SKILL_DIR}/references/architecture-set.md` to shape the view, then `${CLAUDE_SKILL_DIR}/references/mermaid.md` to draw and render it. The architecture branch always renders and reads back. An unrendered fence is not a figure.
43
+ - Mermaid path: read `${CLAUDE_SKILL_DIR}/references/mermaid.md`.
44
+ - Hand-drawn path: read `${CLAUDE_SKILL_DIR}/references/hand-drawn.md`.
63
45
 
64
- There is no external render to verify a freehand drawing against before it is written. The check here is the stroke-and-fill audit above and a read of the markup against what the subject means to show, done once rather than looped. Hand-placed coordinates can drift out of line where a layout engine would not, so a freehand figure bound for a rendered page also takes Step 7's in-page capture.
65
-
66
- ## Step 6: wrap, caption, and verify
46
+ ## Step 5: wrap, caption, and verify
67
47
 
68
48
  - Wrap the drawing in a `<figure>` element with a `<figcaption>` naming what to take from the figure, never what it shows. Let it run wider than the surrounding prose column.
69
- - On the Mermaid path, read the rendered PNG back and judge it against what the figure means to say, the same check `draft-diagram` Steps 5 and 6 run for the architecture surface. Fix the mermaid source and re-render on a defect. Stop after two correction passes. When a defect survives, keep the figure and name the defect in the output rather than reporting a false verification.
70
- - On either path, confirm the figure still reads once every color and font it depends on is stripped to its fallback, per the standard's own working-figure bar.
71
- - When the render fails for any reason, no browser engine, no network, no package manager, continue to Step 7 and name the skipped check. A missing renderer degrades the loop rather than failing it.
72
- - When the destination is a rendered page, an HTML file a browser loads, the check that counts is the figure inside that page, which Step 7 runs once the figure is written. The standalone PNG is measured in the renderer's own fonts and can read clean while the page clips it. A markdown destination has no page to load, so the standalone check above is the whole check there.
49
+ - On the Mermaid path, run the read-back `mermaid.md` states before confirming.
50
+ - On every path, confirm the figure still reads once every color and font it depends on is stripped to its fallback, per the standard's own working-figure bar.
51
+ - When the destination is a rendered page, an HTML file a browser loads, the check that counts is the figure inside that page, which Step 6 captures once the figure is written, on either path. A markdown destination has no page to load, so the checks above are the whole check there.
73
52
 
74
- ## Step 7: confirm and write
53
+ ## Step 6: confirm and write
75
54
 
76
- Show the destination path, the decided render path, and the full `<figure>` markup before writing. Confirm with the user, since the graph-shaped decision and the render verdict are judgment calls with no diff to preview either against.
55
+ Show the destination path, the decided path, and the full `<figure>` markup before writing. Confirm with the user, since the path decision and the render verdict are judgment calls with no diff to preview either against.
77
56
 
78
57
  Write the figure into the destination document at the location the user named.
79
58
 
80
- When the destination is a rendered page, capture the figure inside it and read the capture against what the figure means to say:
81
-
82
- ```bash
83
- canon capture <destination> --selector figure --out .canon/tmp/figures/
84
- ```
85
-
86
- Narrow the selector to this figure, such as by an id on its `<figure>`, when the page carries more than one. Fix a clipped label, a misaligned shape, or an arrow that misses its target in the destination and capture again, within the same two correction passes Step 6 allows. A clipped group title on the Mermaid path takes the scoped overflow style Step 4 states. When the capture fails, keep the figure and name the skipped check in the output, the same way Step 6 treats a failed render.
59
+ When the destination is a rendered page, read the `## Capture in the page` section of `${CLAUDE_SKILL_DIR}/references/hand-drawn.md` and run it, on the Mermaid path as well as the hand-drawn one, within the same two correction passes the read-back allows.
87
60
 
88
- Renders and captures under `.canon/tmp/figures/` are verification artifacts, and the scratch PNGs, the stamps the capture writes beside them, and the JSON config are deleted once the checks confirm, since only the SVG ships.
61
+ Renders and captures under `.canon/tmp/figures/` are verification artifacts, and the scratch PNGs, the stamps the capture writes beside them, and the JSON config are deleted once the checks confirm, since only the SVG ships. Keep the `.mmd` source, which is what a later refresh redraws from.
89
62
 
90
63
  ## Output
91
64
 
92
65
  ### Preview
93
66
 
94
67
  **Destination:** `<path>`
95
- **Render path:** `<mermaid | freehand>`, `<one-sentence reason>`
68
+ **Render path:** `<mermaid | architecture | freehand>`, `<one-sentence reason>`
96
69
 
97
70
  ```html
98
71
  <drafted figure markup></drafted>
@@ -102,7 +75,7 @@ Renders and captures under `.canon/tmp/figures/` are verification artifacts, and
102
75
 
103
76
  ```plaintext
104
77
  ✅ Drafted figure in <path>
105
- Render path: <mermaid | freehand>
78
+ Render path: <mermaid | architecture | freehand>
106
79
  Verified <in page | as a standalone render>. <defect or skipped check, one line, omitted when clean>
107
80
  <Font not embedded: run `canon design css --figures` to ship Virgil and Excalifont. Omitted when the project already ran it.>
108
81
  ```
@@ -0,0 +1,35 @@
1
+ ---
2
+ title: Architecture set
3
+ description: The five architecture views with each one's question, source signal, and diagram type, the code-scan fallback, cited paths, and what moves to canon/context/ instead, read by draft-figure when a request names an architecture view
4
+ ---
5
+
6
+ # Architecture set
7
+
8
+ `draft-figure` reads this file when the request names an architecture view. It shapes the subject, then the figure goes through `${CLAUDE_SKILL_DIR}/references/mermaid.md` like any other graph-shaped figure. The destination stays the one the user named.
9
+
10
+ ## The five views
11
+
12
+ Draw the view the request names. Each answers one question and is drawn from one source signal:
13
+
14
+ | View | Question it answers | Source signal | Diagram type |
15
+ | -------------- | ------------------------------------------------------- | ------------------------------------------------- | ------------------------------------ |
16
+ | System context | Who uses the system, what it talks to, where it ends | `canon/REQUIREMENTS.md` | `flowchart TB` |
17
+ | Components | The layered structure inside the boundary | `canon/ARCHITECTURE.md` | `flowchart TB` with `subgraph` lanes |
18
+ | Request flow | A request lifecycle, an agent loop, or an actor handoff | the entry point and its handlers | `sequenceDiagram` |
19
+ | Data pipeline | Retrieval, ranking, queues, or ETL | the pipeline's modules | `flowchart TB` |
20
+ | Deployment | Hosts, services, and infrastructure config | deploy config, compose files, infrastructure code | `flowchart TB` |
21
+
22
+ System context is the only view that draws the world outside the boundary, and the one a reader outside the team opens first, so assume no knowledge of the repository there.
23
+
24
+ Stay inside `flowchart` and `sequenceDiagram`. C4, state, ER, and class diagrams render inconsistently across viewers.
25
+
26
+ ## Sources
27
+
28
+ - Read `canon/REQUIREMENTS.md`, `canon/ARCHITECTURE.md`, `CLAUDE.md`, and the language manifest (`package.json`, `pyproject.toml`, `Cargo.toml`) when present, plus the top-level folder layout through `ls`. Do not recurse speculatively.
29
+ - When neither planning file exists and no folder structure names a boundary, stop: `❌ No source signal for <view>. Add canon/ARCHITECTURE.md or run inside a project folder.`
30
+ - When the view came from a code scan rather than planning prose, open the caption with `Source: code.` and add `Fidelity is lower than prose-driven figures. Verify against the project's intent.`
31
+
32
+ ## Caption and prose
33
+
34
+ - Name one or two code paths a reader can open, in the caption or the prose beside the figure. Cite only paths that exist and spell each one exactly, since a reader deciding whether the figure still holds starts by opening them.
35
+ - Keep implementation detail out of the figure: function names and call signatures, library versions, config keys and environment variable names, retry counts and timeouts, and workarounds needing more than a sentence. That detail belongs in a `canon/context/` entry, which the caption points at by path when a reader needs the mechanism.
@@ -0,0 +1,31 @@
1
+ ---
2
+ title: Hand-drawn path
3
+ description: Authoring a freehand SVG, its color and accessibility audit, and the in-page capture loop a rendered destination takes, read by draft-figure once a figure is drawn by hand
4
+ ---
5
+
6
+ # Hand-drawn path
7
+
8
+ `draft-figure` reads this file when Step 3 routes a figure to a freehand drawing: a teach lesson whatever its subject, or a subject no flowchart reduces to.
9
+
10
+ ## Draw
11
+
12
+ - Author the SVG by hand: plain shapes and lines authored directly in the markup.
13
+ - Follow the `## Color and accessibility` section of `${CLAUDE_SKILL_DIR}/../../standards/figures.md` for every fill, stroke, and accessible name.
14
+
15
+ There is no external render to verify a freehand drawing against before it is written. The check here is the stroke-and-fill audit above and a read of the markup against what the subject means to show, done once rather than looped. Hand-placed coordinates can drift out of line where a layout engine would not, so a freehand figure bound for a rendered page also takes the in-page capture below.
16
+
17
+ ## Capture in the page
18
+
19
+ When the destination is a rendered page, an HTML file a browser loads, capture the figure inside it once it is written and read the capture against what the figure means to say:
20
+
21
+ ```bash
22
+ canon capture <destination> --selector figure --out .canon/tmp/figures/
23
+ ```
24
+
25
+ - Narrow the selector to this figure, such as by an id on its `<figure>`, when the page carries more than one.
26
+ - Fix a clipped label, a misaligned shape, or an arrow that misses its target in the destination and capture again. Stop after two correction passes.
27
+ - When the capture fails, keep the figure and name the skipped check in the output.
28
+
29
+ A Mermaid figure bound for a rendered page takes this same capture, since its standalone PNG is measured in the renderer's own fonts and can read clean while the page clips it. A clipped group title there takes the scoped overflow style `${CLAUDE_SKILL_DIR}/references/mermaid.md` states.
30
+
31
+ A markdown destination has no page to load, so the read of the markup, or of the standalone render on the Mermaid path, is the whole check there.
@@ -0,0 +1,55 @@
1
+ ---
2
+ title: Mermaid path
3
+ description: The hand-drawn render config, the SVG and PNG pair, the hex rewrite, the scoped overflow fallback, the label check, and the PNG read-back with its two-pass stop, read by draft-figure once a figure routes through Mermaid
4
+ ---
5
+
6
+ # Mermaid path
7
+
8
+ `draft-figure` reads this file when Step 3 routes a figure through Mermaid, whether the subject is graph-shaped or an architecture view the architecture set already shaped.
9
+
10
+ ## Draft the fence
11
+
12
+ - Draft the fence against `${CLAUDE_SKILL_DIR}/../../standards/mermaid.md` for direction, node and edge budgets, `accTitle` and `accDescr`, and quoted labels. Do not work the layout or the budgets from memory.
13
+ - Read every node and subgraph label against the banned characters `${CLAUDE_SKILL_DIR}/../../standards/markdown.md` lists. The audit hook skips every line inside a fence, so a label carrying a banned character passes silently while the same character in the caption is caught. Reading the labels is the only gate they have. A colon works as a replacement inside a label.
14
+
15
+ ## Render
16
+
17
+ Render with the hand-drawn look and the figure font stack, writing scratch files under `.canon/tmp/figures/`:
18
+
19
+ ```bash
20
+ mkdir -p .canon/tmp/figures
21
+ cat <<'EOF' >.canon/tmp/figures/<slug>.json
22
+ {"look": "handDrawn", "themeVariables": {"fontFamily": "Virgil, Excalifont, cursive"}}
23
+ EOF
24
+ bunx -y @mermaid-js/mermaid-cli -i .canon/tmp/figures/<slug>.mmd -o .canon/tmp/figures/<slug>.svg -c .canon/tmp/figures/<slug>.json
25
+ bunx -y @mermaid-js/mermaid-cli -i .canon/tmp/figures/<slug>.mmd -o .canon/tmp/figures/<slug>.png -c .canon/tmp/figures/<slug>.json
26
+ ```
27
+
28
+ Render both formats in one pass. The SVG is what ships inside the `<figure>`, and the PNG is what the read-back below judges, since an SVG's markup carries no recoverable spatial meaning.
29
+
30
+ Use `bunx` when bun is available, falling back to `npx -y @mermaid-js/mermaid-cli ...` otherwise.
31
+
32
+ Before the first render in a project, say what is about to block:
33
+
34
+ ```plaintext
35
+ Rendering to verify layout. The first run downloads the Mermaid CLI and takes about 15 seconds.
36
+ ```
37
+
38
+ ## Rewrite the colors
39
+
40
+ Rewrite every stroke, fill, and text color the renderer wrote as a literal hex value into the custom property the host stylesheet defines, per the color rule in `${CLAUDE_SKILL_DIR}/../../standards/figures.md`. Mermaid's own theme has no notion of a custom property, so this is a source edit made to the rendered SVG, not a config option.
41
+
42
+ ## Clipped titles
43
+
44
+ - Expect a group title or a long label to clip once the destination shows it in the hand font, since the renderer sized its box in a fallback face.
45
+ - When the in-page check shows a clipped title, add a `<style>` inside the SVG, scoped to that SVG's own id, that sets `overflow: visible` on the clipped label's `foreignObject` and centers the label within it, so the wider text spills past both edges evenly. The style hides the mismatch for a title and does nothing for a node label crowding its box, which takes a shorter label instead.
46
+
47
+ ## Fonts
48
+
49
+ Check whether the project ran `canon design css --figures`. The figure and figcaption styling ships unconditionally either way, but that flag alone embeds the Virgil and Excalifont font files. Without it, the figure falls back to the browser's own generic `cursive` face rather than the intended hand-drawn font, and the output names that gap when the flag was not passed.
50
+
51
+ ## Read the render back
52
+
53
+ - Read the rendered PNG and judge it against what the figure means to say, applying the verification properties in the mermaid standard. A source satisfying every rule there can still render as a picture asserting something false, so the image is what gets judged.
54
+ - Fix the Mermaid source and re-render on a defect. Stop after two correction passes. When a defect survives, keep the figure and name the defect in the output rather than reporting a false verification.
55
+ - When the render fails for any reason, no browser engine, no network, no package manager, continue to the confirm step and name the skipped check. A missing renderer degrades the loop rather than failing it.