@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.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/backlog-triage/REQUIREMENT.md +2 -2
- package/claude/skills/backlog-triage/SKILL.md +19 -6
- package/claude/skills/canon-operator/REQUIREMENT.md +1 -0
- package/claude/skills/canon-operator/SKILL.md +5 -1
- package/claude/skills/canvas/REQUIREMENT.md +49 -0
- package/claude/skills/canvas/SKILL.md +106 -0
- package/claude/skills/design-extract/REQUIREMENT.md +1 -1
- package/claude/skills/draft-doc/REQUIREMENT.md +2 -1
- package/claude/skills/draft-doc/references/readme/agent-facing.md +59 -0
- package/claude/skills/draft-doc/references/readme/application.md +66 -0
- package/claude/skills/draft-doc/references/readme/cli.md +64 -0
- package/claude/skills/draft-doc/references/readme/library.md +57 -0
- package/claude/skills/draft-doc/references/readme/plugin.md +66 -0
- package/claude/skills/draft-doc/references/readme.md +33 -4
- package/claude/skills/draft-figure/REQUIREMENT.md +14 -3
- package/claude/skills/draft-figure/SKILL.md +24 -51
- package/claude/skills/draft-figure/references/architecture-set.md +35 -0
- package/claude/skills/draft-figure/references/hand-drawn.md +31 -0
- package/claude/skills/draft-figure/references/mermaid.md +55 -0
- package/claude/skills/git-followup/SKILL.md +17 -6
- package/claude/skills/git-pr/references/evidence.md +4 -2
- package/claude/skills/git-split/SKILL.md +1 -1
- package/claude/skills/index-lookup/SKILL.md +2 -2
- package/claude/skills/review-address/SKILL.md +6 -13
- package/claude/skills/review-address/references/rest-reads.md +43 -0
- package/claude/skills/review-craft/references/ui.md +7 -1
- package/claude/skills/review-pr/REQUIREMENT.md +1 -0
- package/claude/skills/review-pr/SKILL.md +9 -3
- package/claude/skills/review-pr/references/close-out.md +2 -2
- package/claude/skills/review-pr/references/post.md +17 -5
- package/claude/skills/review-pr/references/scope.md +10 -8
- package/claude/skills/review-ui/SKILL.md +12 -4
- package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +30 -1
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +4 -0
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +4 -4
- package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +6 -0
- package/claude/skills/role-orchestrator/scripts/poll.ts +90 -27
- package/claude/skills/role-orchestrator/scripts/repo.ts +3 -5
- package/claude/skills/role-orchestrator/scripts/watch.ts +4 -7
- package/docs/agents/canvas.md +6 -2
- package/docs/agents/commands.md +45 -45
- package/docs/agents/context-audit-checks.md +2 -2
- package/docs/agents/index.md +0 -1
- package/docs/agents/key-changes.md +3 -3
- package/docs/agents/pr-evidence.md +11 -2
- package/docs/agents/pr-reads.md +5 -4
- package/docs/target/projects.md +4 -4
- package/docs/workflow/ai-workflow.md +0 -1
- package/docs/workflow/skill-map.md +3 -3
- package/docs/workflow/visual-design-workflow.md +2 -1
- package/governance/renames.toml +0 -4
- package/governance/rules/standards/markdown.md +1 -0
- package/package.json +1 -2
- package/scripts/core/install-check.sh +1 -1
- package/src/audits/catalog.ts +2 -2
- package/src/canvas/client/inspector/field.tsx +200 -0
- package/src/canvas/client/inspector/fields.css +151 -0
- package/src/canvas/client/inspector/section.tsx +27 -0
- package/src/canvas/client/inspector/values.ts +60 -0
- package/src/canvas/client/inspector.tsx +192 -111
- package/src/canvas/client/shell.css +0 -94
- package/src/canvas/tokens.ts +9 -1
- package/src/claude/cases/misc.ts +5 -0
- package/src/claude/cases/workflow.ts +1 -1
- package/src/claude/seeds.ts +0 -1
- package/src/commands/canvas.ts +29 -0
- package/src/commands/design.ts +2 -64
- package/src/commands/pr.ts +219 -202
- package/src/context/audit.ts +7 -6
- package/src/design/budget.ts +149 -0
- package/src/design/document.ts +22 -10
- package/src/design/render.ts +11 -1
- package/src/design/tokens.ts +40 -61
- package/src/gate/measures.ts +49 -0
- package/src/gate/stages.ts +5 -1
- package/src/help.ts +2 -2
- package/src/migrate/skill-names.ts +7 -1
- package/src/pr/evidence.ts +25 -5
- package/src/pr/rest.ts +137 -0
- package/standards/canvas.md +112 -0
- package/standards/context.md +2 -1
- package/standards/design.md +13 -3
- package/standards/figures.md +0 -1
- package/standards/index.md +1 -1
- package/standards/markdown.md +1 -0
- package/standards/mermaid.md +1 -7
- package/tooling/claude/reference.md +0 -5
- package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +1 -1
- package/claude/skills/draft-diagram/REQUIREMENT.md +0 -45
- package/claude/skills/draft-diagram/SKILL.md +0 -177
- package/docs/agents/design-board.md +0 -33
- package/governance/rules/standards/diagrams.md +0 -14
- package/governance/rules/standards/mermaid.md +0 -15
- package/scripts/core/check-gallery-exclusion.sh +0 -50
- package/src/design/board.ts +0 -576
- package/standards/diagrams.md +0 -132
- 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
|
+
```
|
package/standards/context.md
CHANGED
|
@@ -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
|
-
-
|
|
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
|
package/standards/design.md
CHANGED
|
@@ -33,10 +33,20 @@ Does not govern:
|
|
|
33
33
|
|
|
34
34
|
## Format
|
|
35
35
|
|
|
36
|
-
- Use tables for token systems, one row per token.
|
|
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
|
```
|
package/standards/figures.md
CHANGED
|
@@ -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
|
package/standards/index.md
CHANGED
|
@@ -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
|
package/standards/markdown.md
CHANGED
|
@@ -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
|
package/standards/mermaid.md
CHANGED
|
@@ -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
|
|
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,
|
|
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.
|