@erclx/canon 5.23.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 (98) 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/canon-operator/REQUIREMENT.md +1 -0
  5. package/claude/skills/canon-operator/SKILL.md +5 -1
  6. package/claude/skills/canvas/REQUIREMENT.md +49 -0
  7. package/claude/skills/canvas/SKILL.md +106 -0
  8. package/claude/skills/design-extract/REQUIREMENT.md +1 -1
  9. package/claude/skills/draft-doc/REQUIREMENT.md +2 -1
  10. package/claude/skills/draft-doc/references/readme/agent-facing.md +59 -0
  11. package/claude/skills/draft-doc/references/readme/application.md +66 -0
  12. package/claude/skills/draft-doc/references/readme/cli.md +64 -0
  13. package/claude/skills/draft-doc/references/readme/library.md +57 -0
  14. package/claude/skills/draft-doc/references/readme/plugin.md +66 -0
  15. package/claude/skills/draft-doc/references/readme.md +33 -4
  16. package/claude/skills/draft-figure/REQUIREMENT.md +14 -3
  17. package/claude/skills/draft-figure/SKILL.md +24 -51
  18. package/claude/skills/draft-figure/references/architecture-set.md +35 -0
  19. package/claude/skills/draft-figure/references/hand-drawn.md +31 -0
  20. package/claude/skills/draft-figure/references/mermaid.md +55 -0
  21. package/claude/skills/git-followup/SKILL.md +17 -6
  22. package/claude/skills/git-pr/references/evidence.md +4 -2
  23. package/claude/skills/git-split/SKILL.md +1 -1
  24. package/claude/skills/index-lookup/SKILL.md +2 -2
  25. package/claude/skills/review-address/SKILL.md +6 -13
  26. package/claude/skills/review-address/references/rest-reads.md +43 -0
  27. package/claude/skills/review-craft/references/ui.md +7 -1
  28. package/claude/skills/review-pr/REQUIREMENT.md +1 -0
  29. package/claude/skills/review-pr/SKILL.md +9 -3
  30. package/claude/skills/review-pr/references/close-out.md +2 -2
  31. package/claude/skills/review-pr/references/post.md +17 -5
  32. package/claude/skills/review-pr/references/scope.md +10 -8
  33. package/claude/skills/review-ui/SKILL.md +12 -4
  34. package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +30 -1
  35. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +4 -0
  36. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +4 -4
  37. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +6 -0
  38. package/claude/skills/role-orchestrator/scripts/poll.ts +90 -27
  39. package/claude/skills/role-orchestrator/scripts/repo.ts +3 -5
  40. package/claude/skills/role-orchestrator/scripts/watch.ts +4 -7
  41. package/docs/agents/canvas.md +6 -2
  42. package/docs/agents/commands.md +45 -45
  43. package/docs/agents/context-audit-checks.md +2 -2
  44. package/docs/agents/index.md +0 -1
  45. package/docs/agents/key-changes.md +3 -3
  46. package/docs/agents/pr-evidence.md +11 -2
  47. package/docs/agents/pr-reads.md +5 -4
  48. package/docs/target/projects.md +4 -4
  49. package/docs/workflow/ai-workflow.md +0 -1
  50. package/docs/workflow/skill-map.md +3 -3
  51. package/docs/workflow/visual-design-workflow.md +2 -1
  52. package/governance/renames.toml +0 -4
  53. package/governance/rules/standards/markdown.md +1 -0
  54. package/package.json +1 -2
  55. package/scripts/core/install-check.sh +1 -1
  56. package/src/audits/catalog.ts +2 -2
  57. package/src/canvas/client/inspector/field.tsx +200 -0
  58. package/src/canvas/client/inspector/fields.css +151 -0
  59. package/src/canvas/client/inspector/section.tsx +27 -0
  60. package/src/canvas/client/inspector/values.ts +60 -0
  61. package/src/canvas/client/inspector.tsx +192 -111
  62. package/src/canvas/client/shell.css +0 -94
  63. package/src/canvas/tokens.ts +9 -1
  64. package/src/claude/cases/misc.ts +5 -0
  65. package/src/claude/cases/workflow.ts +1 -1
  66. package/src/claude/seeds.ts +0 -1
  67. package/src/commands/canvas.ts +29 -0
  68. package/src/commands/design.ts +2 -64
  69. package/src/commands/pr.ts +219 -202
  70. package/src/context/audit.ts +7 -6
  71. package/src/design/budget.ts +149 -0
  72. package/src/design/document.ts +22 -10
  73. package/src/design/render.ts +11 -1
  74. package/src/design/tokens.ts +40 -61
  75. package/src/gate/measures.ts +49 -0
  76. package/src/gate/stages.ts +5 -1
  77. package/src/help.ts +2 -2
  78. package/src/migrate/skill-names.ts +7 -1
  79. package/src/pr/evidence.ts +25 -5
  80. package/src/pr/rest.ts +137 -0
  81. package/standards/canvas.md +112 -0
  82. package/standards/context.md +2 -1
  83. package/standards/design.md +13 -3
  84. package/standards/figures.md +0 -1
  85. package/standards/index.md +1 -1
  86. package/standards/markdown.md +1 -0
  87. package/standards/mermaid.md +1 -7
  88. package/tooling/claude/reference.md +0 -5
  89. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +1 -1
  90. package/claude/skills/draft-diagram/REQUIREMENT.md +0 -45
  91. package/claude/skills/draft-diagram/SKILL.md +0 -177
  92. package/docs/agents/design-board.md +0 -33
  93. package/governance/rules/standards/diagrams.md +0 -14
  94. package/governance/rules/standards/mermaid.md +0 -15
  95. package/scripts/core/check-gallery-exclusion.sh +0 -50
  96. package/src/design/board.ts +0 -576
  97. package/standards/diagrams.md +0 -132
  98. package/tooling/claude/seeds/.claude/diagrams/index.md +0 -8
package/src/pr/rest.ts ADDED
@@ -0,0 +1,137 @@
1
+ /**
2
+ * The REST shapes `canon pr` reads a pull request through, put into the shapes
3
+ * its readers already take.
4
+ *
5
+ * A cloud session's GitHub proxy refuses GraphQL and serves REST, and every
6
+ * `gh pr view` runs on GraphQL. The verbs therefore read the pulls, reviews,
7
+ * and issue-comments endpoints, and this module is the one boundary where
8
+ * those payloads meet the field names the rest of `src/pr/` was written
9
+ * against.
10
+ */
11
+ import type { ReviewRow } from '@/pr/review-scope'
12
+
13
+ /** One row of `repos/{owner}/{repo}/pulls/<n>` or of the pulls listing. */
14
+ export interface RestPull {
15
+ readonly number?: number
16
+ readonly body?: string | null
17
+ readonly head?: { readonly ref?: string; readonly sha?: string }
18
+ readonly base?: { readonly ref?: string }
19
+ readonly mergeable_state?: string
20
+ }
21
+
22
+ /** One row of `repos/{owner}/{repo}/pulls/<n>/reviews`. */
23
+ export interface RestReview {
24
+ readonly body?: string | null
25
+ readonly commit_id?: string | null
26
+ readonly submitted_at?: string | null
27
+ }
28
+
29
+ /** One row of `repos/{owner}/{repo}/issues/<n>/comments`. */
30
+ export interface RestComment {
31
+ readonly html_url?: string
32
+ readonly body?: string | null
33
+ }
34
+
35
+ /** The head branch, reported head, and base of the pull request a caller named. */
36
+ export interface PullIdentity {
37
+ readonly number: number | undefined
38
+ readonly branch: string
39
+ readonly head?: string
40
+ readonly base?: string
41
+ readonly mergeState?: string
42
+ }
43
+
44
+ export type BranchPull =
45
+ | { readonly kind: 'found'; readonly number: number }
46
+ | {
47
+ readonly kind: 'refused'
48
+ readonly reason: 'gh-failed' | 'ambiguous-pull'
49
+ }
50
+
51
+ /**
52
+ * Spells `mergeable_state` the way GraphQL's `mergeStateStatus` spells it.
53
+ *
54
+ * The two vocabularies hold the same values in different cases, so the
55
+ * mapping is a case change. `unknown` stays `UNKNOWN`, which `withMergeState`
56
+ * already reads as not conflicted while GitHub computes the state lazily.
57
+ */
58
+ export function mergeStateOf(state: string | undefined): string | undefined {
59
+ return state === undefined || state === '' ? undefined : state.toUpperCase()
60
+ }
61
+
62
+ export function identityOf(row: RestPull): PullIdentity {
63
+ const mergeState = mergeStateOf(row.mergeable_state)
64
+ return {
65
+ number: row.number,
66
+ branch: row.head?.ref ?? '',
67
+ ...(row.head?.sha !== undefined && { head: row.head.sha }),
68
+ ...(row.base?.ref !== undefined && { base: row.base.ref }),
69
+ ...(mergeState !== undefined && { mergeState }),
70
+ }
71
+ }
72
+
73
+ /**
74
+ * The one open pull request a branch carries.
75
+ *
76
+ * Two open pull requests on one head against different bases refuse rather
77
+ * than resolving to the first, since the listing's order says nothing about
78
+ * which one the caller meant.
79
+ */
80
+ export function selectBranchPull(rows: readonly RestPull[]): BranchPull {
81
+ const numbers = rows
82
+ .map((row) => row.number)
83
+ .filter((number): number is number => number !== undefined)
84
+ if (numbers.length > 1) return { kind: 'refused', reason: 'ambiguous-pull' }
85
+ const [number] = numbers
86
+ return number === undefined
87
+ ? { kind: 'refused', reason: 'gh-failed' }
88
+ : { kind: 'found', number }
89
+ }
90
+
91
+ /**
92
+ * A pending review carries no `submitted_at`, which reads as null the way the
93
+ * GraphQL listing reported it.
94
+ */
95
+ export function reviewRowOf(review: RestReview): ReviewRow {
96
+ return {
97
+ body: review.body ?? '',
98
+ commit:
99
+ review.commit_id === undefined || review.commit_id === null
100
+ ? null
101
+ : { oid: review.commit_id },
102
+ submittedAt: review.submitted_at ?? null,
103
+ }
104
+ }
105
+
106
+ /** `html_url` ends in the same `#issuecomment-<id>` anchor GraphQL's `url` did. */
107
+ export function commentRowOf(comment: RestComment): {
108
+ readonly url?: string
109
+ readonly body: string
110
+ } {
111
+ return {
112
+ ...(comment.html_url !== undefined && { url: comment.html_url }),
113
+ body: comment.body ?? '',
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Parses the output of `gh api --paginate --jq '.[] | @json'`, one compact
119
+ * object per line across every page.
120
+ *
121
+ * A filter that reshapes each row makes `--paginate` emit one value per page
122
+ * rather than one merged array, so a line per row is the form that survives
123
+ * paging. A line that does not parse refuses the whole listing, since a list
124
+ * short by one review reads as a thread missing its newest pass.
125
+ */
126
+ export function parseJsonLines<T>(stdout: string): T[] | undefined {
127
+ const rows: T[] = []
128
+ for (const line of stdout.split('\n')) {
129
+ if (line.trim() === '') continue
130
+ try {
131
+ rows.push(JSON.parse(line) as T)
132
+ } catch {
133
+ return undefined
134
+ }
135
+ }
136
+ return rows
137
+ }
@@ -0,0 +1,112 @@
1
+ ---
2
+ title: Canvas reference
3
+ description: Page folders, frame files, the layout.json shape, token resolution, and what a frame must not assume on a local design canvas
4
+ ---
5
+
6
+ # Canvas reference
7
+
8
+ Applies to the canvas content under `.canon/canvas/`, where each page is a folder of HTML frames and a layout file placing them. A frame is a draft drawn to be looked at and picked from, so it changes whenever someone tries a direction, and the operator clears it once the direction lands where the project keeps its design.
9
+
10
+ The folder is gitignored, so nothing in a repository checks its shape and nothing backs it up by default.
11
+
12
+ ## Scope
13
+
14
+ Governs the canvas content under `.canon/canvas/`: page folder names, frame files, the `layout.json` shape, how tokens reach a frame, and what a frame must not assume about where it renders.
15
+
16
+ Does not govern:
17
+
18
+ - The design decisions a frame is drafting, such as type, color, and layout taste: the project's own design document
19
+ - The project's design document and the token module it renders from, which a picked direction is carried into: `design.md`
20
+ - The procedure that starts the canvas, reads the operator's selection, and carries a pick back, which belongs to the surface driving the canvas
21
+ - Voice, rhythm, and sentence construction in any text a frame shows: the `write-human` skill
22
+
23
+ ## What a working canvas looks like
24
+
25
+ A canvas works when the operator and a session can point at the same thing and see the same thing:
26
+
27
+ - Which pages exist, and which frames sit on each?
28
+ - Where does each frame sit on its page, and at what width does it render?
29
+ - Does each frame show the project's own tokens rather than values copied into it?
30
+ - Does a frame render the same in the live canvas as in a capture?
31
+
32
+ A canvas failing these is non-conforming even when it satisfies every shape rule below.
33
+
34
+ ## Pages
35
+
36
+ - Make one folder per page directly under `.canon/canvas/`. A page groups the frames compared side by side, such as the candidates for one decision.
37
+ - Name a page as one path segment of letters, digits, dots, dashes, and underscores, starting with a letter or a digit. A name opening with a dot is hidden from every listing.
38
+ - Never write a folder whose name opens with a dot. Those names are reserved for files the canvas generates and removes itself.
39
+ - Rename a page rather than copying it under a new name, so its layout and frames stay together.
40
+
41
+ ## Frames
42
+
43
+ - Write one frame as one `.html` file inside its page folder. The filename without its extension is the frame's name, and the name follows the page naming rule.
44
+ - Write each frame as a whole document, with a doctype, a `head`, and a `body`. A fragment gets elements the browser builds around it, and an address the operator picks inside it can no longer be matched to the file.
45
+ - Write every table with its `tbody` stated, for the same reason. A browser adds one the file leaves out.
46
+ - Keep a frame self-contained. Inline its own styles and scripts, or link a file inside the same page folder, since a frame is served from its page and nothing outside the canvas resolves.
47
+ - Use the project's custom properties, `var(--<name>)`, for every value the token stylesheet defines. A literal copied from it shows the right color today and the wrong one after the next token change.
48
+ - Edit a frame by rewriting its file. The file is the frame, and nothing else holds a copy of its markup.
49
+
50
+ ## Layout
51
+
52
+ - Keep one `layout.json` per page, beside its frames. It holds each frame's box and nothing else, so moving a frame never rewrites its markup.
53
+ - Key each box by the frame's name, under one top-level `frames` object.
54
+ - Give every box four finite numbers in pixels: `x` and `y` for its top-left corner on the page, `width` for the width the frame renders at, and `height` for the height it shows on the canvas.
55
+ - Leave a frame out of the layout only when its position does not matter yet. A reader places an unnamed frame in a row after the others, at the default size of 1440 by 900.
56
+ - Never write the file while a writer holds its lock, which is the same name with `.lock` appended. Two writers each replacing the whole file lose one of the two edits.
57
+ - Treat a layout that does not parse as a defect to repair, never as an empty one to overwrite. Writing over it erases every box it held.
58
+
59
+ The keys below are the ones a reader parses. The frame names are placeholders.
60
+
61
+ ```json
62
+ {
63
+ "frames": {
64
+ "hero-a": { "x": 0, "y": 0, "width": 1440, "height": 900 },
65
+ "hero-b": { "x": 1520, "y": 0, "width": 1440, "height": 900 }
66
+ }
67
+ }
68
+ ```
69
+
70
+ ## Tokens
71
+
72
+ - Expect the token stylesheet first in every frame's `head` when it is served, ahead of anything the frame links. A stylesheet the frame links itself still wins the cascade.
73
+ - Read where the tokens come from in this order: the canvas tool's own module inside its own checkout, else the project's installed base stylesheet plus anything in its project override folder, else none. A canvas with no token source renders unstyled and says so.
74
+ - Never paste the token stylesheet into a frame. The injected copy follows the project, and a pasted one stops following it the moment it is written.
75
+
76
+ ## What a frame must not assume
77
+
78
+ - Do not assume a viewport. A frame renders at its layout width on the canvas and in a capture alike, so size it with media queries against that width rather than against the operator's window.
79
+ - Do not assume the theme. The canvas theme toggle is the operator's own state, and a capture shows each frame in its default theme.
80
+ - Do not assume a network. Name only fonts the machine has, or ship the font beside the frame, since a capture refuses a frame naming a font it cannot find.
81
+ - Do not assume a default font. Set `font-family` on the `html` element with an installed family first, since a capture checks the root's first family and the browser's default serif is absent from many machines.
82
+ - Do not assume the frame is the source. A picked direction is carried into the project's design document or its wireframes, and the operator clears the frame once it has been carried.
83
+ - Do not assume the canvas survives the checkout. The folder is gitignored, so a lost checkout loses every page in it.
84
+
85
+ ## Template
86
+
87
+ A new frame starts from this skeleton. The names in it are placeholders.
88
+
89
+ ```html
90
+ <!doctype html>
91
+ <html lang="en">
92
+ <head>
93
+ <meta charset="utf-8" />
94
+ <title>frame-name</title>
95
+ <style>
96
+ html {
97
+ font-family: installed-family, sans-serif;
98
+ }
99
+ main {
100
+ padding: var(--space-lg);
101
+ color: var(--color-text);
102
+ background: var(--color-background);
103
+ }
104
+ </style>
105
+ </head>
106
+ <body>
107
+ <main>
108
+ <h1>frame-name</h1>
109
+ </main>
110
+ </body>
111
+ </html>
112
+ ```
@@ -21,7 +21,8 @@ Does not govern:
21
21
  - Decisions filling a stack, delivery, boundary, layout, or build-principle slot, and system-wide risks: `architecture.md`
22
22
  - Product scope, goals, and non-goals: `requirements.md`
23
23
  - Path-scoped coding rules: `rule.md`
24
- - Diagrams and wireframes, which answer structure and layout questions an entry hands off: `diagrams.md` and `wireframes.md`
24
+ - Wireframes, which answer layout questions an entry hands off: `wireframes.md`
25
+ - Figures and architecture views, which answer structure questions an entry hands off: the `draft-figure` skill
25
26
  - The consumer-facing reference under `docs/`, whose reader is operating the project without the source open: `docs.md`
26
27
 
27
28
  ## Organizing principle
@@ -33,10 +33,20 @@ Does not govern:
33
33
 
34
34
  ## Format
35
35
 
36
- - Use tables for token systems, one row per token. Use short bullets for component rules, one decision per line.
36
+ - Use tables for token systems, one row per token. Write every other rule as a short bullet, one decision per line, within `## Prose budget`.
37
37
  - Plain English over technical notation. If a section could be removed and the developer would still build correctly from wireframes and code alone, remove it.
38
38
  - Keep table headers and role names intact so the render tooling can parse the token tables.
39
39
 
40
+ ## Prose budget
41
+
42
+ The record is a lookup, so its prose stays short enough to scan beside the tables. Why a value was chosen belongs in the project's context entry for design, never here.
43
+
44
+ - Personality: one paragraph of at most 60 words
45
+ - Any other section: at most 3 rules beside its table, each at most 25 words. A bullet is one rule, and so is a paragraph.
46
+ - The lead before the first section: at most 40 words
47
+
48
+ Headings and table rows are never counted, and a section holding a table and no rules is within budget. A bullet wrapping over several lines counts as one rule, and a nested bullet counts as its own.
49
+
40
50
  ## The uncertainty tag
41
51
 
42
52
  A cell no source anchors ends in ` ? verify`, written inside the cell rather than as a trailing column, since a trailing marker breaks the table parse. A cell wrapping itself in a code span carries the tag inside the span, as in `` `#ffffff ? verify` ``. Both spellings parse.
@@ -93,9 +103,9 @@ The column headers are the strings the renderer parses, read by exact key, so th
93
103
 
94
104
  ## Motion
95
105
 
96
- <whether motion is used at all, and if so the default duration and easing>
106
+ - <whether motion is used at all, and if so the default duration and easing>
97
107
 
98
108
  ## Iconography
99
109
 
100
- <style, source library, and whether custom icons are allowed>
110
+ - <style, source library, and whether custom icons are allowed>
101
111
  ```
@@ -14,7 +14,6 @@ Governs a hand-drawn figure wherever one is authored, naming no path because an
14
14
  Does not govern:
15
15
 
16
16
  - Direction, layout, node and edge budgets, labels, and render verification for the Mermaid path: `mermaid.md`
17
- - The toolkit's own architecture-diagram surface at `.canon/diagrams/`, which shares no code, font, or visual language with this convention: `diagrams.md`
18
17
  - Language, word choice, punctuation, and formatting in the caption or surrounding prose: `markdown.md`
19
18
  - Voice, rhythm, and sentence construction in that prose: the `write-human` skill
20
19
  - Which document types may carry a figure, and what else they require: that document type's own standard
@@ -10,10 +10,10 @@ Reference docs for consistent authoring across the toolkit and target projects.
10
10
  - [Architecture reference](architecture.md): Shape and content rules for canon/ARCHITECTURE.md
11
11
  - [Board reference](board.md): Readiness groups, row cells, and ordering for priority.md, the unordered backlog.md beside it, and the generated index under .canon/tasks/
12
12
  - [Branch reference](branch.md): Branch naming format and type conventions
13
+ - [Canvas reference](canvas.md): Page folders, frame files, the layout.json shape, token resolution, and what a frame must not assume on a local design canvas
13
14
  - [Commit reference](commit.md): Commit message format and type conventions
14
15
  - [Context entry reference](context.md): Shape and content rules for canon/context/<domain>.md entries
15
16
  - [Design reference](design.md): Shape and content rules for canon/DESIGN.md
16
- - [Diagram reference](diagrams.md): Shape and content rules for .canon/diagrams/<kind>.md files
17
17
  - [Docs reference](docs.md): Reader and jurisdiction, frontmatter, page structure, what a page links out to, the diagram permission, and when a category earns a subfolder
18
18
  - [Figure reference](figures.md): When a figure earns its place, the render-first policy for a graph-shaped subject, freehand SVG as the escape hatch, and its wrapping, color, and accessibility rules
19
19
  - [Glossary reference](glossary.md): Frontmatter, entry shape, ordering, and the rules deciding which terms a glossary carries
@@ -7,6 +7,7 @@ rule:
7
7
  - 'Read it before a substantial markdown edit. Do not work the banned characters from memory.'
8
8
  - 'Run `canon markdown audit <path>` after the edit, and rewrite the sentence carrying a hit rather than swapping the token for a near-synonym.'
9
9
  - 'Voice, rhythm, and sentence construction are a separate topic. `800-prose` routes them.'
10
+ - 'Load the `canon:draft-figure` skill before drafting or revising a Mermaid fence or a figure, and report it rather than proceeding silently when it does not resolve.'
10
11
  ---
11
12
 
12
13
  # Markdown reference
@@ -1,12 +1,6 @@
1
1
  ---
2
2
  title: Mermaid reference
3
3
  description: Direction and layout, node and edge budgets, accessibility fields, label punctuation, and render verification for a Mermaid diagram
4
- paths:
5
- - '**/*.md'
6
- rule:
7
- - 'Apply this rule only when the edit drafts or revises a Mermaid fence. Skip it on a markdown edit carrying no diagram.'
8
- - 'Read it before drafting the fence. Do not work the layout or the budgets from memory.'
9
- - 'Which diagram a document carries and what question it answers is a separate topic. The diagrams standard routes it.'
10
4
  ---
11
5
 
12
6
  # Mermaid reference
@@ -19,7 +13,7 @@ Governs the Mermaid attribute wherever a fence is written, naming no path becaus
19
13
 
20
14
  Does not govern:
21
15
 
22
- - Which diagram a document carries, what question it answers, its frontmatter, and the explanation prose beneath it: `diagrams.md`
16
+ - Which architecture view a figure draws and what question it answers: the `draft-figure` skill's architecture set
23
17
  - Language, word choice, punctuation, and formatting in the prose around a fence: `markdown.md`
24
18
  - Voice, rhythm, and sentence construction in that prose: the `write-human` skill
25
19
  - The mechanism behind any component a diagram draws: `context.md`
@@ -23,7 +23,6 @@ canon/
23
23
 
24
24
  .canon/
25
25
  ├── tasks/ ← seeded. One file per task plus a generated index.md, local scratch only. `archive/` holds the tasks that shipped.
26
- ├── diagrams/ ← seeded. Per-kind Mermaid views. `index.md` is the discovery anchor. `<kind>.md` files hold one diagram each, grouped by the `category` frontmatter field.
27
26
  ├── plans/ ← execution detail for multi-step tasks. `feature-*.md` entries swept by context-fold into `archive/`.
28
27
  ├── review/ ← one subfolder per producer. `branch/` for review-branch, `feedback/` for canon feedback, `memory/` for memory-review with its own `archive/`, `design/` for canon design render.
29
28
  ├── tmp/ ← ephemeral scratch space, safe to delete without loss
@@ -60,10 +59,6 @@ Convert by hand, once per project:
60
59
  2. Replace `MEMORY.md` with an `index.md` carrying `title` and `subtitle` frontmatter and nothing else.
61
60
  3. Run `canon indexes regen --no-stage --root . .canon/memory/index.md` to build the catalog, and compare its entry count against the file count before deleting anything.
62
61
 
63
- ## Upgrading from a single-file diagram set
64
-
65
- A project installed before the diagram surface became a folder still holds `.claude/DIAGRAMS.md`. Unlike the board, this one migrates itself. The `draft-diagram` skill reads the flat file when `.canon/diagrams/` holds no entries, splits it by kind into the folder, and reports what it wrote. The old file stays on disk so the split can be compared against its source, and deleting it is a manual step once that check passes.
66
-
67
62
  ## Gitignore
68
63
 
69
64
  - `# Claude`: `.canon/`, `.claude/worktrees/`, `.claude/.tmp/` <!-- canon-keep-record-root -->
@@ -57,5 +57,5 @@ marker="$marker_dir/$key"
57
57
  mkdir -p "$marker_dir"
58
58
  : >"$marker"
59
59
 
60
- msg='Temporary file write outside the project scratch folder. Write temp files to .canon/tmp/<slug>/ in the project root, or .claude/.tmp/<slug>/ where the project carries no .canon/ root, not system temp. See the Scratch rule (604-scratch, under canon/ in your installed governance rules).'
60
+ msg='Temporary file write outside the project scratch folder. Write temp files to .canon/tmp/<slug>/ in the project root, not system temp. See the Scratch rule (604-scratch, under canon/ in your installed governance rules).'
61
61
  jq -nc --arg msg "$msg" '{hookSpecificOutput:{hookEventName:"PreToolUse",additionalContext:$msg}}'
@@ -1,45 +0,0 @@
1
- ---
2
- name: draft-diagram
3
- description: Why diagrams are a folder of per-kind entries rather than one file, and why the verified marker is spent only on a render that was read back
4
- ---
5
-
6
- # Draft diagram requirement
7
-
8
- ## Gap
9
-
10
- Without this skill, a session asked to refresh one view rewrites the whole surface, so four diagrams nobody checked change in the same commit and the reviewer cannot tell which one the pass was about. It picks a filename of its own, and the next refresh writes a duplicate beside the old entry instead of finding it. It reaches for C4, state, or class diagrams that render differently in every viewer.
11
-
12
- The verification failure is the expensive one. A mermaid source can satisfy every rule in the standard and still render as a picture that asserts something false about the system, so a session that judges the source and stamps the entry as verified has spent the only signal a reader has on a diagram nobody looked at. The standards hook toggles off inside a fenced block, so a banned character in a node label passes silently while the same character one line below is caught.
13
-
14
- ## Must
15
-
16
- - Write one entry per kind under the filename and category the standard fixes, so a later refresh finds its target
17
- - Pick which entries this pass writes from whether the source signal exists and whether the pass has reason to touch it
18
- - Stay inside `flowchart` and `sequenceDiagram`
19
- - Render every entry this pass wrote and read the image back against what the entry means to say
20
- - Stamp the verified marker only on an entry whose render was read back and judged correct
21
- - Name every skipped render and every surviving defect in the output
22
- - Regenerate the catalog from sibling frontmatter after the last entry is written
23
- - Read the node labels by hand, since no hook checks inside a fence
24
-
25
- ## Must not
26
-
27
- - Rewrite an entry this pass had no reason to touch, or report one as written when it was left alone
28
- - Stamp an untouched entry, a skipped render, or an entry whose defect survived
29
- - Report a clean verification when a check did not run
30
- - Commit a render, which is a verification artifact rather than a deliverable
31
- - Redraw a diagram during a migration pass, since a rewrite and a move landing together leaves no way to tell which one broke it
32
- - Loop on a defect past two correction passes
33
-
34
- ## Guards
35
-
36
- - No architecture file, no requirements file, and no folder structure to scan: stop, because nothing anchors a diagram
37
- - Render fails for any reason: continue to the output and name the skipped check. A missing renderer degrades the loop rather than failing it.
38
- - `canon` not on PATH: report the catalog as stale rather than hand-editing it
39
-
40
- ## Out of scope
41
-
42
- - Design tokens and the visual system, which `design-extract` owns
43
- - UI roughness, which `ux-audit` owns
44
- - Writing the catalog file, which the index regen owns from frontmatter
45
- - Deleting a pre-split flat diagrams file after a migration, which stays the user's call
@@ -1,177 +0,0 @@
1
- ---
2
- name: draft-diagram
3
- description: Writes per-kind Mermaid diagram entries into `.canon/diagrams/`, covering system context, components, request flow, data pipeline, and deployment. Reads `canon/ARCHITECTURE.md` and `REQUIREMENTS.md` when present, falls back to a code-structure scan. Use when asked to "draw the architecture", "diagram the system", "show the components", "give me a flow chart", "refresh the deploy diagram", or "visualize the project". Do NOT use for design tokens (use `design-extract`) or UI audits (use `ux-audit`).
4
- ---
5
-
6
- # Draft diagram
7
-
8
- Write one entry per diagram kind. Never rewrite the folder wholesale. A pass that refreshes the deploy view leaves the other four files byte-identical, which is the whole reason the surface is a folder.
9
-
10
- ## Guards
11
-
12
- - If no `canon/ARCHITECTURE.md`, no `canon/REQUIREMENTS.md`, and no top-level folder structure to scan, stop: `❌ No source signal. Add canon/ARCHITECTURE.md or run inside a project folder.`
13
-
14
- ## Step 1: read sources and locate the surface
15
-
16
- Read these from the project root in parallel, skipping any that do not exist:
17
-
18
- - `.canon/diagrams/index.md` and every `.canon/diagrams/*.md`: which entries exist and what they already say
19
- - `canon/REQUIREMENTS.md`: users, external dependencies, tech stack, MVP feature list
20
- - `canon/ARCHITECTURE.md`: layered components, key technical decisions
21
- - `CLAUDE.md`: project type, conventions
22
- - `package.json`, `pyproject.toml`, `Cargo.toml`: language and framework markers
23
- - Top-level folder layout and root config files via `ls`: deploy targets, infrastructure config, component boundaries
24
-
25
- Do not recurse speculatively.
26
-
27
- Follow `${CLAUDE_SKILL_DIR}/../../standards/diagrams.md` for frontmatter, entry kinds, and explanation rules. Follow `${CLAUDE_SKILL_DIR}/../../standards/mermaid.md` for what goes inside the fence: layout, budgets, accessibility, labels, and verification.
28
-
29
- ### Migrating a pre-split flat file
30
-
31
- When `.canon/diagrams/` holds no entry and `.claude/DIAGRAMS.md` exists, this pass converts it. An entry is any `*.md` in the folder other than `index.md`. The seed ships `index.md` alone, so a folder holding only the catalog is an empty set and still converts.
32
-
33
- Split each H2 section into the entry whose kind it matches, carry its mermaid body and explanation prose across unchanged, and add the frontmatter the standard requires. Leave `.claude/DIAGRAMS.md` on disk so the split can be compared against its source, and say in Step 7 that deleting it is the user's call.
34
-
35
- Convert only. Do not redraw a diagram during a migration pass, since a rewrite and a move landing together leaves no way to tell which one broke a diagram.
36
-
37
- ## Step 2: pick which entries this pass writes
38
-
39
- Write an entry only when its source signal exists, and only when this pass has a reason to touch it. Skip the rest and leave their files alone.
40
-
41
- Count entries the way Step 1 does, excluding `index.md`. Counting the catalog as an entry sends a first pass down the compare branch with nothing to compare, and it writes nothing.
42
-
43
- - The user named a kind: write that one.
44
- - The user asked broadly and the folder holds no entry: write every kind whose signal exists.
45
- - The user asked broadly and at least one entry exists: compare each entry against its source signal and write only the ones whose signal moved. Report the untouched ones as current.
46
-
47
- The standard fixes one filename and one `category` value per kind. Use them verbatim rather than inventing a name, since a refresh finds its target by filename and a new name writes a duplicate entry beside the old one. Emit `system-context.md` whenever `canon/REQUIREMENTS.md` exists, since it is the entry a reader outside the team opens first and the set is incomplete without it.
48
-
49
- Stay inside `flowchart` and `sequenceDiagram`. C4, state, ER, and class diagrams render inconsistently across viewers.
50
-
51
- ## Step 3: write the entries
52
-
53
- One file per kind at `.canon/diagrams/<kind>.md`. Write only the files Step 2 selected.
54
-
55
- ````markdown
56
- ---
57
- title: <what the entry answers, sentence case>
58
- description: <the question it settles and the signal that drives it>
59
- category: <the kind, matching the standard>
60
- verified: 'TODO: never verified'
61
- ---
62
-
63
- # <Same as title>
64
-
65
- <one-line statement of what the diagram shows>
66
-
67
- ```mermaid
68
- <diagram type>
69
- accTitle: <what the diagram answers>
70
- accDescr: <the structure in one sentence>
71
- <body>
72
- ```
73
-
74
- <one to three explanation paragraphs>
75
- ````
76
-
77
- Write `verified` as the literal `TODO: never verified` here, quoted so the colon parses. Step 6 replaces it once a render has been read back, so a SHA written beside the diagram would assert a check that has not run yet. The placeholder is what keeps a render-less project conforming, since Step 5 sends a failed render past Step 6 and nothing stamps the entry. Carry an existing entry's current value across untouched when this pass is rewriting one, and let Step 6 replace that instead.
78
-
79
- When sources came from a code scan rather than planning prose, lead the explanation with `Source: code.` and add `Fidelity is lower than prose-driven diagrams. Verify against the project's intent.`
80
-
81
- A reader deciding whether the entry still holds starts from the code paths the explanation cites, so cite paths that exist and spell them exactly.
82
-
83
- Quote node labels containing spaces or special characters with double quotes (`A["Web shell"]`). Avoid parentheses inside labels, they break some renderers. Use `<br/>` for line breaks inside labels.
84
-
85
- Run the scan in `${CLAUDE_SKILL_DIR}/../../standards/publish.md` against the whole file, including inside mermaid `subgraph` labels and node text. The file lands in the repository rather than on a remote, so the scan's destination scoping leaves the character checks and not the phase-label one, and the `pull_request` check the git-pr surface carries reads only a pull request's own title and body, never a file the diff carries. A colon also works as a replacement inside a label. The pedagogical voice the diagram standard asks for is a yield on voice alone and buys no exemption from these bans.
86
-
87
- Nothing enforces the ban inside the diagram. The standards-audit hook toggles on a fence and skips every line within it, so a label carrying an em dash passes silently while the same character in the explanation paragraph below is caught. Check the labels by reading them. This is the one place in the file where the author is the only gate.
88
-
89
- ## Step 4: regenerate the catalog
90
-
91
- ```bash
92
- canon indexes regen
93
- ```
94
-
95
- Run it after the last entry is written. It rebuilds `.canon/diagrams/index.md` from sibling frontmatter and groups entries under their `category`. Never hand-edit that file.
96
-
97
- When `canon` is not on PATH, say so in Step 7 and name `.canon/diagrams/index.md` as stale rather than writing it by hand.
98
-
99
- Scaffold `.canon/diagrams/index.md` with `title` and `subtitle` frontmatter before the first regen when the folder is new and the seed did not provide one. The subtitle routes a first-time reader to the system context entry, since the catalog sorts categories alphabetically rather than in narrative order.
100
-
101
- ## Step 5: render what this pass changed
102
-
103
- A source that satisfies every rule in the standard can still render as a picture that asserts something false about the system. Verification runs on the image.
104
-
105
- Render each entry this pass wrote. Skip every entry Step 2 left alone, since an untouched diagram cannot develop a new render defect.
106
-
107
- Before the first render in a project, say what is about to block:
108
-
109
- ```plaintext
110
- Rendering to verify layout. The first run downloads the Mermaid CLI and takes about 15 seconds.
111
- ```
112
-
113
- Write each diagram's mermaid body to its own scratch file, then render it:
114
-
115
- ```bash
116
- mkdir -p .canon/tmp/diagrams && bunx -y @mermaid-js/mermaid-cli -i .canon/tmp/diagrams/<kind>.mmd -o .canon/tmp/diagrams/<kind>.png
117
- ```
118
-
119
- Render to PNG. An SVG export reads back as markup with no recoverable spatial meaning, so it cannot be verified. Use `bunx` when bun is available. Fall back to `npx -y @mermaid-js/mermaid-cli ...` otherwise.
120
-
121
- Renders are verification artifacts, not deliverables. They stay in `.canon/tmp/diagrams/` and are never committed.
122
-
123
- When the render fails for any reason (no browser engine, no network, no package manager), continue to Step 7 and name the skipped check in the chat output. A missing renderer degrades the loop, it does not fail it.
124
-
125
- ## Step 6: read the renders back
126
-
127
- Read each PNG and judge the picture against what the entry means to say. Apply the verification properties in `${CLAUDE_SKILL_DIR}/../../standards/mermaid.md`.
128
-
129
- Fix the source and re-render. Stop after two correction passes on an entry.
130
-
131
- When a defect survives, keep the entry and name the defect in the chat output. A diagram whose author states the flaw is recoverable. A wrong diagram reported as verified is not.
132
-
133
- ### Stamp the marker
134
-
135
- Stamp every entry that passed. Set `verified` to the short SHA of `HEAD` and today's ISO date, and delete any `stale` key the entry carries:
136
-
137
- ```bash
138
- git rev-parse --short HEAD
139
- ```
140
-
141
- The result is one line, `<sha> <date>`, as in `73e9a3f8 2026-08-02`. <!-- canon-allow-reference: illustrates the verified field's two-token format, which a qualified sha would misstate -->
142
-
143
- Stamp nothing else. An entry Step 2 left alone was never re-checked, an entry whose defect survived is known wrong, and an entry whose render was skipped was never seen. All three keep the marker they already carry, and Step 7 names the last two. The field means a render was read back and judged correct, so stamping any of these spends the only signal a reader has on a diagram nobody confirmed.
144
-
145
- A migration pass stamps on the same rule. Conversion writes entries, Step 5 renders them, and this step reads them back, so the marker records the commit the split was checked against rather than the commit the flat file was written at.
146
-
147
- ## Step 7: chat output
148
-
149
- ```plaintext
150
- 📝 Wrote N entries to .canon/diagrams/
151
- .canon/diagrams/<kind>.md
152
- <one line per entry written>
153
-
154
- Left untouched: <kind, kind> <omitted when the folder was empty>
155
-
156
- Verified N renders. <defect or skipped check, one line each, omitted when clean>
157
-
158
- Open in any viewer with native Mermaid support (VS Code with `bierner.markdown-mermaid`, GitHub, Cursor) to render. Ask to export if you want SVG or PNG files.
159
- ```
160
-
161
- Emit the full path for every file written. Do not echo the diagram bodies in chat. Never report a clean verification when a render was skipped or a defect survived. Never report an entry as written when this pass left it alone.
162
-
163
- After a migration pass, add: `Converted .claude/DIAGRAMS.md into N entries. The original is untouched, delete it once you have compared the split against it.`
164
-
165
- ## Step 8: export on request
166
-
167
- If the user asks to export the diagrams (`export to svg`, `give me images`, `render to png`), run:
168
-
169
- ```bash
170
- mkdir -p .canon/tmp/render/diagrams && bunx -y @mermaid-js/mermaid-cli -i .canon/diagrams/<kind>.md -o .canon/tmp/render/diagrams/<kind>.png
171
- ```
172
-
173
- Export PNG by default. Swap the extension for `.svg` only when the user asks for vector, and never for the Step 5 verification path. The CLI writes one file per `mermaid` block, suffixing when an entry holds more than one. Export every entry when the user names no kind. Output line:
174
-
175
- ```plaintext
176
- 📝 Wrote N files to .canon/tmp/render/diagrams/
177
- ```
@@ -1,33 +0,0 @@
1
- ---
2
- title: Design board
3
- description: Generating a static index over a project's design surfaces, what each panel reads, and which two stay toolkit-checkout-only
4
- ---
5
-
6
- # Design board
7
-
8
- `canon design board [--out <path>] [--root <path>]` generates a static page set indexing six design surfaces and reports the path a reader opens with `canon serve`. `--root` defaults to the main worktree the same way `canon teach`'s verbs resolve their own root. The surfaces panel's landing-page half and the whole components panel render only when that root is this toolkit's own checkout, since both read this repository's own build output rather than anything a target project produces. Running the installed CLI from inside a checkout of this project prints a mismatch warning when the caller's working directory disagrees with the root it resolved.
9
-
10
- Each panel reads what is already on disk and reports its own missing source rather than failing the run, so an absent build, an empty corpus, or a toolkit-only gate renders an empty-state message instead of a broken frame.
11
-
12
- ```bash
13
- canon design board
14
- canon serve .canon/tmp/render/board
15
- ```
16
-
17
- | Option | Default | Behavior |
18
- | ------------------ | ------------------------- | --------------------------------------------- |
19
- | `-o, --out <path>` | `.canon/tmp/render/board` | Output directory |
20
- | `--root <path>` | The main worktree | Project root the board reads its sources from |
21
-
22
- ## Panels
23
-
24
- - **Tokens** renders `DESIGN.md` through the same renderer `canon design render` uses, rather than a second one, reading `<root>/canon/DESIGN.md` or `<root>/.claude/DESIGN.md` for a target that has not run `canon migrate surface-roots`.
25
- - **Surfaces** iframes the built landing page from `<root>/web/dist/` and a teach workspace from `<root>/.canon/teach/`, copying each whole into the board's own tree. The landing-page half reports a toolkit-only notice outside this toolkit's own checkout, and either half reports its own missing build or absent workspace rather than rendering a broken frame.
26
- - **Wireframes** reads every `**/*.md` under `<root>/canon/wireframes/` or `<root>/.claude/wireframes/`, excluding `index.md` at any depth, and renders each file as-is inside a `<pre>`, labeled from its own `description` frontmatter field. Reports the whole panel empty rather than per file when the directory is absent or holds nothing to render.
27
- - **Past candidates** lists an arm capture image per folder under `<root>/.canon/picks/`, skipping `references/`, and states the corpus carries none rather than rendering an empty grid.
28
- - **Components** iframes the gallery built by `bun run web:gallery`, a second Astro config at `web/gallery.config.mjs` reading `web/gallery-src/` and writing `web/gallery-dist/`. That config's `srcDir` is never read by `web:build`'s own config, so the gallery never reaches the published `web/dist/`. The gallery page renders every component under `web/src/components/` except two whose props carry no defaults, which it names rather than filling with invented data. Reports a toolkit-only notice outside this toolkit's own checkout, and a missing gallery build otherwise.
29
- - **References** lists whatever image an operator has dropped flat under `<root>/.canon/picks/references/`, with no fetching and no second token source: a reference is an image, nothing more. Reports the folder as absent or as present but carrying no image rather than rendering an empty grid.
30
-
31
- ## What it does not do
32
-
33
- The board never writes outside its own output directory and never builds `web/dist/` on a caller's behalf. `web/dist/` and `.canon/teach/` are read, not written, and copied into the board's tree only for that run: the generator clears and recreates its output directory on every call, so a caller relying on it to persist between runs is relying on an accident. It refuses rather than clearing a directory that is or contains the project root or the caller's own working directory, which is what an `--out .` typo would otherwise do, including when a global install resolves the project root to a different checkout than the one the caller stands in.