@erclx/canon 5.24.0 → 5.26.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 (185) hide show
  1. package/README.md +2 -1
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/backlog-triage/REQUIREMENT.md +2 -2
  4. package/claude/skills/backlog-triage/SKILL.md +19 -6
  5. package/claude/skills/canvas/REQUIREMENT.md +49 -0
  6. package/claude/skills/canvas/SKILL.md +106 -0
  7. package/claude/skills/context-fold/REQUIREMENT.md +6 -2
  8. package/claude/skills/context-fold/SKILL.md +8 -6
  9. package/claude/skills/context-fold/references/anchor-sweep.md +2 -2
  10. package/claude/skills/design-extract/REQUIREMENT.md +1 -1
  11. package/claude/skills/design-taste/REQUIREMENT.md +1 -1
  12. package/claude/skills/design-taste/SKILL.md +4 -2
  13. package/claude/skills/design-taste/references/adopted.md +40 -0
  14. package/claude/skills/design-taste/references/kinds.md +1 -1
  15. package/claude/skills/design-taste/references/systems.md +1 -7
  16. package/claude/skills/design-taste/references/tells.md +2 -24
  17. package/claude/skills/docs-sync/REQUIREMENT.md +2 -1
  18. package/claude/skills/document-health/REQUIREMENT.md +2 -0
  19. package/claude/skills/document-health/SKILL.md +21 -0
  20. package/claude/skills/document-health/references/axes.md +12 -0
  21. package/claude/skills/draft-and-pick/SKILL.md +1 -1
  22. package/claude/skills/draft-doc/REQUIREMENT.md +1 -1
  23. package/claude/skills/draft-figure/REQUIREMENT.md +14 -3
  24. package/claude/skills/draft-figure/SKILL.md +24 -51
  25. package/claude/skills/draft-figure/references/architecture-set.md +35 -0
  26. package/claude/skills/draft-figure/references/hand-drawn.md +31 -0
  27. package/claude/skills/draft-figure/references/mermaid.md +55 -0
  28. package/claude/skills/draft-identity/SKILL.md +1 -1
  29. package/claude/skills/draft-screencast/SKILL.md +9 -7
  30. package/claude/skills/draft-slides/REQUIREMENT.md +17 -12
  31. package/claude/skills/draft-slides/SKILL.md +31 -20
  32. package/claude/skills/git-followup/SKILL.md +17 -6
  33. package/claude/skills/git-issue/references/issue.md +10 -0
  34. package/claude/skills/git-pr/references/evidence.md +12 -2
  35. package/claude/skills/git-pr/references/labels.md +12 -0
  36. package/claude/skills/git-split/SKILL.md +19 -8
  37. package/claude/skills/index-lookup/SKILL.md +2 -2
  38. package/claude/skills/markdown-propose/references/format.md +9 -0
  39. package/claude/skills/memory-review/references/later-phases.md +7 -0
  40. package/claude/skills/review-address/SKILL.md +6 -13
  41. package/claude/skills/review-address/references/rest-reads.md +43 -0
  42. package/claude/skills/review-craft/references/ui.md +7 -1
  43. package/claude/skills/review-pr/REQUIREMENT.md +1 -0
  44. package/claude/skills/review-pr/SKILL.md +9 -3
  45. package/claude/skills/review-pr/references/close-out.md +2 -2
  46. package/claude/skills/review-pr/references/post.md +17 -5
  47. package/claude/skills/review-pr/references/scope.md +10 -8
  48. package/claude/skills/review-ui/SKILL.md +12 -4
  49. package/claude/skills/review-ui/references/post.md +4 -2
  50. package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +38 -1
  51. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +13 -0
  52. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +4 -0
  53. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +4 -4
  54. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +6 -0
  55. package/claude/skills/role-orchestrator/scripts/poll.ts +90 -27
  56. package/claude/skills/role-orchestrator/scripts/repo.ts +3 -5
  57. package/claude/skills/role-orchestrator/scripts/watch.ts +4 -7
  58. package/claude/skills/search-craft/REQUIREMENT.md +1 -1
  59. package/claude/skills/search-craft/SKILL.md +3 -1
  60. package/claude/skills/search-craft/references/adopted.md +30 -0
  61. package/claude/skills/target-setup/references/detect.md +8 -0
  62. package/claude/skills/target-setup/references/indexes.md +13 -0
  63. package/claude/skills/target-setup/references/verify.md +8 -0
  64. package/claude/skills/test-first/SKILL.md +2 -0
  65. package/claude/skills/test-first/references/adopted.md +36 -0
  66. package/claude/skills/write-human/SKILL.md +1 -1
  67. package/claude/skills/write-human/references/machine-tells.md +13 -0
  68. package/docs/agents/canvas.md +4 -2
  69. package/docs/agents/capture.md +1 -1
  70. package/docs/agents/commands.md +47 -47
  71. package/docs/agents/context-audit-checks.md +2 -2
  72. package/docs/agents/index.md +2 -3
  73. package/docs/agents/key-changes.md +3 -3
  74. package/docs/agents/pr-evidence.md +11 -2
  75. package/docs/agents/pr-reads.md +5 -4
  76. package/docs/agents/records-stale.md +22 -9
  77. package/docs/agents/records.md +1 -1
  78. package/docs/agents/sandbox.md +1 -1
  79. package/docs/agents/skills-audit.md +10 -3
  80. package/docs/agents/slides.md +15 -3
  81. package/docs/target/projects.md +4 -4
  82. package/docs/workflow/ai-workflow.md +1 -2
  83. package/docs/workflow/index.md +1 -0
  84. package/docs/workflow/parallel-features.md +1 -1
  85. package/docs/workflow/skill-map-on-request.md +42 -0
  86. package/docs/workflow/skill-map.md +3 -36
  87. package/docs/workflow/visual-design-workflow.md +1 -0
  88. package/governance/renames.toml +0 -4
  89. package/governance/rules/standards/markdown.md +1 -0
  90. package/package.json +1 -2
  91. package/scripts/core/install-check.sh +1 -1
  92. package/scripts/lib/sandbox-dispatch.sh +1 -1
  93. package/scripts/lib/sandbox-git.sh +1 -9
  94. package/scripts/lib/sandbox-path.sh +1 -1
  95. package/scripts/sandbox-hook.sh +99 -0
  96. package/src/audits/catalog.ts +2 -2
  97. package/src/canvas/capture.ts +1 -9
  98. package/src/canvas/client/inspector/color-field.tsx +368 -0
  99. package/src/canvas/client/inspector/field.tsx +200 -0
  100. package/src/canvas/client/inspector/fields.css +292 -0
  101. package/src/canvas/client/inspector/section.tsx +27 -0
  102. package/src/canvas/client/inspector/values.ts +183 -0
  103. package/src/canvas/client/inspector.tsx +176 -160
  104. package/src/canvas/client/shell.css +0 -101
  105. package/src/capture/render.ts +45 -7
  106. package/src/capture/sources.ts +43 -0
  107. package/src/census/count.ts +1 -1
  108. package/src/claude/cases/misc.ts +5 -0
  109. package/src/claude/cases/workflow.ts +6 -1
  110. package/src/claude/seeds.ts +1 -2
  111. package/src/claude/skills-audit.ts +68 -14
  112. package/src/client-commands.ts +1 -1
  113. package/src/commands/claude.ts +27 -2
  114. package/src/commands/design.ts +2 -64
  115. package/src/commands/feedback.ts +3 -3
  116. package/src/commands/pr.ts +219 -202
  117. package/src/commands/records.ts +88 -6
  118. package/src/commands/sandbox.ts +50 -10
  119. package/src/commands/slides.ts +31 -77
  120. package/src/context/audit.ts +8 -7
  121. package/src/design/tokens.ts +2 -18
  122. package/src/docs/read.ts +1 -1
  123. package/src/{github.ts → feedback/github.ts} +1 -1
  124. package/src/gate/measures.ts +6 -4
  125. package/src/gov/citations.ts +1 -1
  126. package/src/gov/payload.ts +1 -1
  127. package/src/gov/superseded.ts +2 -2
  128. package/src/help.ts +2 -2
  129. package/src/markdown/ceiling.ts +1 -1
  130. package/src/migrate/skill-names.ts +7 -1
  131. package/src/pr/evidence.ts +25 -5
  132. package/src/pr/rest.ts +137 -0
  133. package/src/record-root.ts +1 -0
  134. package/src/records/backup.ts +1 -0
  135. package/src/records/push-guard.ts +1 -1
  136. package/src/records/stale-canonical.ts +146 -0
  137. package/src/records/stale.ts +1 -1
  138. package/src/sandbox/hooks.ts +128 -0
  139. package/src/sandbox/provision.ts +596 -0
  140. package/src/sandbox/tree.ts +103 -4
  141. package/src/secrets/marker.ts +2 -2
  142. package/src/secrets/scan.ts +1 -1
  143. package/src/shipped/references.ts +2 -2
  144. package/src/slides/convert/export.ts +2 -16
  145. package/src/slides/decks.ts +110 -0
  146. package/src/standards/read.ts +1 -1
  147. package/src/sync/seeds-report.ts +1 -1
  148. package/src/tasks/answers.ts +1 -1
  149. package/src/tasks/archive.ts +1 -1
  150. package/src/teach/nav.ts +1 -1
  151. package/src/tooling/inject.ts +1 -1
  152. package/src/web/readme-citations.ts +1 -1
  153. package/standards/architecture.md +1 -0
  154. package/standards/canvas.md +112 -0
  155. package/standards/context.md +2 -1
  156. package/standards/figures.md +0 -1
  157. package/standards/index.md +1 -1
  158. package/standards/markdown.md +1 -0
  159. package/standards/mermaid.md +1 -7
  160. package/standards/requirements.md +2 -0
  161. package/standards/skill-practice.md +2 -1
  162. package/standards/skill.md +1 -0
  163. package/tooling/claude/reference.md +0 -5
  164. package/claude/skills/draft-diagram/REQUIREMENT.md +0 -45
  165. package/claude/skills/draft-diagram/SKILL.md +0 -177
  166. package/docs/agents/design-board.md +0 -33
  167. package/governance/rules/standards/diagrams.md +0 -14
  168. package/governance/rules/standards/mermaid.md +0 -15
  169. package/scripts/core/check-gallery-exclusion.sh +0 -50
  170. package/scripts/manage-sandbox.sh +0 -472
  171. package/src/design/board.ts +0 -576
  172. package/src/slides/layouts.ts +0 -478
  173. package/src/slides/parse.ts +0 -84
  174. package/src/slides/render.ts +0 -110
  175. package/src/slides/styles.ts +0 -96
  176. package/standards/diagrams.md +0 -132
  177. package/tooling/claude/seeds/.claude/diagrams/index.md +0 -8
  178. /package/claude/skills/write-human/references/{source-material.md → adopted.md} +0 -0
  179. /package/src/{commands/feedback-format.ts → feedback/body.ts} +0 -0
  180. /package/src/{github-format.ts → feedback/github-format.ts} +0 -0
  181. /package/src/{frontmatter.ts → markdown/frontmatter.ts} +0 -0
  182. /package/src/{binary.ts → sweep/binary.ts} +0 -0
  183. /package/src/{exempt-marker.ts → sweep/exempt-marker.ts} +0 -0
  184. /package/src/{paths.ts → tasks/paths.ts} +0 -0
  185. /package/src/{seed-marker.ts → tooling/seed-marker.ts} +0 -0
@@ -1,9 +1,9 @@
1
- import { isMarked } from '@/exempt-marker'
1
+ import { isMarked } from '@/sweep/exempt-marker'
2
2
 
3
3
  /**
4
4
  * The inline token exempting one line from the secret scan.
5
5
  *
6
- * Shaped on the `stub: true` precedent in `src/seed-marker.ts`, which answers
6
+ * Shaped on the `stub: true` precedent in `src/tooling/seed-marker.ts`, which answers
7
7
  * a check whose own comment records a false-positive class. The exemption
8
8
  * travels with the line it exempts rather than sitting in a path list away
9
9
  * from it, so a reader meeting a muted match finds the reason on the spot.
@@ -1,6 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises'
2
2
  import { join } from 'node:path'
3
- import { isBinary } from '@/binary'
3
+ import { isBinary } from '@/sweep/binary'
4
4
  import { listRepositoryFiles } from '@/git-files'
5
5
  import { isExempt } from '@/secrets/marker'
6
6
  import { matchLine } from '@/secrets/patterns'
@@ -1,6 +1,6 @@
1
1
  import { existsSync } from 'node:fs'
2
2
  import { join } from 'node:path'
3
- import { isMarked } from '@/exempt-marker'
3
+ import { isMarked } from '@/sweep/exempt-marker'
4
4
 
5
5
  /**
6
6
  * The corpora a target reader reaches, which is the `files` field less `src`.
@@ -113,7 +113,7 @@ const COMMIT_SHA = /(?<![0-9A-Za-z_@/#])([0-9a-f]{7,40})(?![0-9A-Za-z_])/g
113
113
  * one.
114
114
  *
115
115
  * The literal is hardcoded rather than read from `package.json` or
116
- * `git remote`, matching `src/github-format.ts` and `src/commands/repo.ts`,
116
+ * `git remote`, matching `src/feedback/github-format.ts` and `src/commands/repo.ts`,
117
117
  * which already hardcode it.
118
118
  *
119
119
  * Same word-boundary discipline as the two patterns above: a word character
@@ -1,10 +1,4 @@
1
- import {
2
- copyFileSync,
3
- mkdirSync,
4
- readdirSync,
5
- readFileSync,
6
- writeFileSync,
7
- } from 'node:fs'
1
+ import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
8
2
  import { basename, join } from 'node:path'
9
3
  import { fileURLToPath, pathToFileURL } from 'node:url'
10
4
  import JSZip from 'jszip'
@@ -42,6 +36,7 @@ import {
42
36
  type WalkedSlide,
43
37
  walkSlide,
44
38
  } from '@/slides/convert/walk'
39
+ import { slideFiles } from '@/slides/decks'
45
40
  import { embedFonts, type FontNotice } from '@/slides/package/fonts'
46
41
  import {
47
42
  type EntranceSpec,
@@ -127,15 +122,6 @@ function refused(reason: ExportRefusal, message: string): ExportResult {
127
122
  return { status: 'refused', reason, message }
128
123
  }
129
124
 
130
- /** Every `.html` file directly in the folder, in filename order. */
131
- export function slideFiles(sourceDir: string): string[] {
132
- return readdirSync(sourceDir, { withFileTypes: true })
133
- .filter((entry) => entry.isFile() && entry.name.endsWith('.html'))
134
- .map((entry) => entry.name)
135
- .sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
136
- .map((name) => join(sourceDir, name))
137
- }
138
-
139
125
  /** A picture whose bytes the post-write pass replaces or whose corners it rounds. */
140
126
  interface Patch {
141
127
  readonly slide: number
@@ -0,0 +1,110 @@
1
+ import { existsSync, readdirSync, statSync } from 'node:fs'
2
+ import { join, relative, resolve } from 'node:path'
3
+ import { creationRel, recordDir } from '@/record-root'
4
+ import { readDeck } from '@/slides/convert/deck'
5
+
6
+ /**
7
+ * A deck is a folder under the record root's `slides/`, holding its `.html`
8
+ * slides, an optional `deck.json`, and its assets. `layouts/` beside the decks
9
+ * holds the project's shared layouts and is never a deck itself.
10
+ */
11
+ export const SLIDES_FOLDER = 'slides'
12
+
13
+ export const LAYOUTS_FOLDER = 'layouts'
14
+
15
+ export interface Deck {
16
+ readonly name: string
17
+ /** From `deck.json`, falling back to the folder name as the export does. */
18
+ readonly title: string
19
+ readonly slides: number
20
+ /** Relative to the project root. */
21
+ readonly path: string
22
+ }
23
+
24
+ export type DeckRefusal = 'no-decks' | 'ambiguous' | 'no-deck'
25
+
26
+ export type DeckResolution =
27
+ | { readonly status: 'resolved'; readonly dir: string }
28
+ | {
29
+ readonly status: 'refused'
30
+ readonly reason: DeckRefusal
31
+ readonly message: string
32
+ }
33
+
34
+ /** Every `.html` file directly in the folder, in filename order. */
35
+ export function slideFiles(dir: string): string[] {
36
+ return readdirSync(dir, { withFileTypes: true })
37
+ .filter((entry) => entry.isFile() && entry.name.endsWith('.html'))
38
+ .map((entry) => entry.name)
39
+ .sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
40
+ .map((name) => join(dir, name))
41
+ }
42
+
43
+ export function listDecks(root: string): Deck[] {
44
+ const dir = recordDir(root, SLIDES_FOLDER)
45
+ if (!isDirectory(dir)) return []
46
+
47
+ return readdirSync(dir, { withFileTypes: true })
48
+ .filter((entry) => entry.isDirectory() && entry.name !== LAYOUTS_FOLDER)
49
+ .map((entry) => entry.name)
50
+ .sort((a, b) => a.localeCompare(b))
51
+ .flatMap((name) => {
52
+ const deckDir = join(dir, name)
53
+ const slides = slideFiles(deckDir).length
54
+ if (slides === 0) return []
55
+ const read = readDeck(deckDir)
56
+ const title = read.status === 'read' ? read.deck.title : name
57
+ return [{ name, title, slides, path: relative(root, deckDir) }]
58
+ })
59
+ }
60
+
61
+ /**
62
+ * The folder `render` reads. A deck name wins over a folder of the same name
63
+ * relative to the root, and no name at all takes the only deck there is.
64
+ */
65
+ export function resolveDeck(
66
+ root: string,
67
+ target: string | undefined,
68
+ ): DeckResolution {
69
+ const decks = listDecks(root)
70
+ const at = creationRel(root, SLIDES_FOLDER)
71
+
72
+ if (target === undefined) {
73
+ const [only, ...rest] = decks
74
+ if (only === undefined) {
75
+ return refuse('no-decks', `No deck under ${at}/. Draw one there first.`)
76
+ }
77
+ if (rest.length > 0) {
78
+ return refuse(
79
+ 'ambiguous',
80
+ `${decks.length} decks under ${at}/, name one: ${names(decks)}`,
81
+ )
82
+ }
83
+ return { status: 'resolved', dir: join(root, only.path) }
84
+ }
85
+
86
+ const named = decks.find((deck) => deck.name === target)
87
+ if (named) return { status: 'resolved', dir: join(root, named.path) }
88
+
89
+ const folder = resolve(root, target)
90
+ if (isDirectory(folder)) return { status: 'resolved', dir: folder }
91
+
92
+ return refuse(
93
+ 'no-deck',
94
+ decks.length === 0
95
+ ? `${target} is neither a deck under ${at}/ nor a folder`
96
+ : `${target} is neither a deck nor a folder. Decks: ${names(decks)}`,
97
+ )
98
+ }
99
+
100
+ function names(decks: readonly Deck[]): string {
101
+ return decks.map((deck) => deck.name).join(', ')
102
+ }
103
+
104
+ function refuse(reason: DeckRefusal, message: string): DeckResolution {
105
+ return { status: 'refused', reason, message }
106
+ }
107
+
108
+ function isDirectory(path: string): boolean {
109
+ return existsSync(path) && statSync(path).isDirectory()
110
+ }
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readdirSync, readFileSync } from 'node:fs'
2
2
  import { basename, join } from 'node:path'
3
- import { stripFrontmatter } from '@/frontmatter'
3
+ import { stripFrontmatter } from '@/markdown/frontmatter'
4
4
  import { PROJECT_ROOT } from '@/project-root'
5
5
 
6
6
  export const INDEX_FILE = 'index.md'
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from 'node:fs'
2
2
  import { relative } from 'node:path'
3
3
  import { planSeeds, type Seed } from '@/claude/seeds'
4
- import { rewritesOnInstall, stripSeedMarker } from '@/seed-marker'
4
+ import { rewritesOnInstall, stripSeedMarker } from '@/tooling/seed-marker'
5
5
  import { findInstalledOrigin, readHistoryIndex } from '@/sync/history'
6
6
 
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from 'node:fs'
2
2
  import { readFile } from 'node:fs/promises'
3
3
  import { isAbsolute, relative, resolve } from 'node:path'
4
- import { isUnder } from '@/paths'
4
+ import { isUnder } from '@/tasks/paths'
5
5
  import { recordDir, recordDirs } from '@/record-root'
6
6
  import {
7
7
  hasStagedBatches,
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs'
2
2
  import { mkdir, readdir, readFile, rename, writeFile } from 'node:fs/promises'
3
3
  import { basename, dirname, join, relative, resolve, sep } from 'node:path'
4
4
  import { regenOne } from '@/indexes/regen'
5
- import { isUnder } from '@/paths'
5
+ import { isUnder } from '@/tasks/paths'
6
6
  import { recordDir, recordDirs } from '@/record-root'
7
7
 
8
8
  const TASKS = 'tasks'
package/src/teach/nav.ts CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  } from '@/design/components'
8
8
  import { buildDesignCss } from '@/design/css'
9
9
  import { FAVICON_COLORS, faviconLink, renderFavicon } from '@/design/favicon'
10
- import { stripFrontmatter } from '@/frontmatter'
10
+ import { stripFrontmatter } from '@/markdown/frontmatter'
11
11
  import { parseFrontmatter, readField } from '@/indexes/frontmatter'
12
12
  import { PROJECT_ROOT } from '@/project-root'
13
13
  import { compileScript } from '@/teach/browser/compile'
@@ -3,7 +3,7 @@ import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises'
3
3
  import { dirname, join, relative } from 'node:path'
4
4
  import { $ } from 'bun'
5
5
  import { copyPreservingMode } from '@/copy'
6
- import { rewritesOnInstall, stripSeedMarker } from '@/seed-marker'
6
+ import { rewritesOnInstall, stripSeedMarker } from '@/tooling/seed-marker'
7
7
  import { resolveSurfacePath } from '@/surface-root'
8
8
  import { mergeSections, pruneSections } from '@/tooling/gitignore'
9
9
  import {
@@ -1,4 +1,4 @@
1
- import { isMarked } from '@/exempt-marker'
1
+ import { isMarked } from '@/sweep/exempt-marker'
2
2
 
3
3
  export const README_PARAPHRASE_MARKER = 'canon-allow-readme-paraphrase'
4
4
 
@@ -54,6 +54,7 @@ Use `## Overview`, `## Key technical decisions` with one named H3 per decision,
54
54
 
55
55
  - Rewrite a decision a later one changed rather than appending the change beside it. A reader should find the design that stands in one place, with the alternative that lost stated once.
56
56
  - Hold only what is open under `## Risks / open questions`. An entry leaves the section in the change that settles it, becoming a decision here when it fills a slot and moving to the domain context entry it constrains when it does not.
57
+ - Carry an optional `reviewed: YYYY-MM-DD` frontmatter field naming the day someone last read the record whole for drift. Whoever finishes that review sets it as the review's last edit, and `canon records stale canonical` reads it to count the releases shipped since. A record with no field reads as never reviewed.
57
58
 
58
59
  ## Verification anchors
59
60
 
@@ -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` with an installed family first on an element the frame's text inherits from, since a capture checks the first family of the text it renders 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
@@ -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`
@@ -46,6 +46,8 @@ This file loads into every session, so its weight is paid before any work starts
46
46
 
47
47
  Once every entry in the MVP list, or in a later scope section, has shipped, delete the section. Git keeps the old text, and the document states the project as it stands. Do not annotate an entry as shipped and leave it in place. While any entry is unshipped, do not renumber the list or append to it.
48
48
 
49
+ The record may carry an optional `reviewed: YYYY-MM-DD` frontmatter field naming the day someone last read it whole for identity drift. Whoever finishes that review sets it as the review's last edit, and `canon records stale canonical` reads it to count the releases shipped since. A record with no field reads as never reviewed.
50
+
49
51
  Later scope arrives as a new section rather than as an extension of the MVP list. Name the section for what it delivers and state its entries as outcomes, the same way the goals are stated. Nothing sequences either list into versions. Work reaches the board as discrete tasks under `tasks.md`, and `board.md` orders them by readiness, so a section here states what is wanted and never when it lands.
50
52
 
51
53
  ## Distribution
@@ -47,5 +47,6 @@ A practice skill failing any of these is non-conforming even when every heading
47
47
  ## Which skills are practice skills
48
48
 
49
49
  - `canon claude skills audit` keeps the list of practice skills and checks each listed one for the three closing sections and the ledger.
50
+ - It keeps a wider ledger list beside it, holding every practice skill plus the skills that teach a craft drawn from outside sources without a practice's closing shape. A skill on the wider list alone owes the ledger and none of the three sections.
50
51
  - Never mark a skill as a practice skill in its own frontmatter, since a self-declared kind lets a skill exempt itself.
51
- - The list names this toolkit's shipped skills alone, so a project's own practice skill takes this shape with no check behind it.
52
+ - Both lists name this toolkit's shipped skills alone, so a project's own practice skill takes this shape with no check behind it.
@@ -130,6 +130,7 @@ allowed-tools: <tools required>
130
130
  - Below the ceiling, name the branch that skips a block before moving it. A block every run dereferences costs a read and saves nothing.
131
131
  - Past the ceiling, move a whole step to a reference, preferring a step a named branch skips. Keep the step heading in the body with one line naming the file to read on reaching it, so the body still shows every step in order.
132
132
  - Name every file under `references/` in the body at the point a session reads it, since an unnamed reference is never loaded.
133
+ - Open a file under `references/` running over 100 lines with a `## Contents` H2 of anchor links after its H1 and intro, so a session that reads only the top of the file still sees everything it holds.
133
134
  - Keep the trigger, the skip condition, and the guard in the body. A run that never reaches the block has to decide that without opening the reference.
134
135
  - Never point one skill at a sibling skill's folder for a reference both read. Each skill carries its own copy under `references/`, generated rather than hand-copied by the rule in `## Path resolution`.
135
136
 
@@ -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 -->
@@ -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