@erclx/canon 5.34.0 → 5.36.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 (248) hide show
  1. package/README.md +15 -17
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/REQUIREMENT.md +1 -1
  4. package/claude/skills/auto-ship/references/draft-mark.md +1 -1
  5. package/claude/skills/canon-cli/REQUIREMENT.md +2 -2
  6. package/claude/skills/canon-cli/SKILL.md +12 -12
  7. package/claude/skills/canvas-board/REQUIREMENT.md +1 -1
  8. package/claude/skills/canvas-board/SKILL.md +2 -2
  9. package/claude/skills/canvas-board/references/design.md +53 -36
  10. package/claude/skills/codebase-layout/REQUIREMENT.md +1 -1
  11. package/claude/skills/codebase-layout/SKILL.md +2 -2
  12. package/claude/skills/create-rule/REQUIREMENT.md +6 -8
  13. package/claude/skills/create-rule/SKILL.md +8 -8
  14. package/claude/skills/create-rule/references/rule.md +11 -23
  15. package/claude/skills/create-skill/SKILL.md +2 -1
  16. package/claude/skills/create-skill/references/skill-paths.md +12 -15
  17. package/{standards/standard.md → claude/skills/create-skill/references/skill-reference.md} +50 -45
  18. package/claude/skills/create-skill/references/skill.md +9 -3
  19. package/claude/skills/design-taste/REQUIREMENT.md +6 -6
  20. package/claude/skills/design-taste/SKILL.md +2 -2
  21. package/claude/skills/design-taste/references/craft.md +1 -1
  22. package/claude/skills/design-taste/references/preflight.md +2 -2
  23. package/claude/skills/design-taste/references/tells.md +1 -1
  24. package/claude/skills/draft-and-pick/SKILL.md +1 -1
  25. package/claude/skills/draft-identity/SKILL.md +3 -2
  26. package/claude/skills/git-commit/references/versioning.md +13 -13
  27. package/claude/skills/git-pr/SKILL.md +1 -1
  28. package/claude/skills/git-pr/references/publish.md +1 -1
  29. package/claude/skills/markdown-craft/references/markdown.md +3 -2
  30. package/claude/skills/plan-feature/SKILL.md +1 -1
  31. package/claude/skills/review-address/REQUIREMENT.md +1 -1
  32. package/claude/skills/review-address/SKILL.md +2 -2
  33. package/claude/skills/review-craft/references/ui.md +1 -1
  34. package/claude/skills/review-pr/SKILL.md +3 -3
  35. package/claude/skills/review-pr/references/post.md +1 -1
  36. package/claude/skills/review-ui/REQUIREMENT.md +1 -1
  37. package/claude/skills/review-ui/SKILL.md +1 -1
  38. package/claude/skills/role-orchestrator/REQUIREMENT.md +2 -2
  39. package/claude/skills/role-orchestrator/SKILL.md +11 -12
  40. package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +3 -1
  41. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +11 -8
  42. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +10 -4
  43. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +2 -2
  44. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +11 -12
  45. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +12 -15
  46. package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +6 -4
  47. package/claude/skills/role-orchestrator/scripts/poll.ts +50 -1
  48. package/claude/skills/role-reviewer/REQUIREMENT.md +17 -7
  49. package/claude/skills/role-reviewer/SKILL.md +17 -10
  50. package/claude/skills/role-worker/REQUIREMENT.md +1 -1
  51. package/claude/skills/role-worker/SKILL.md +6 -6
  52. package/claude/skills/session-compact/references/handoff-note.md +1 -1
  53. package/claude/skills/target-setup/SKILL.md +2 -2
  54. package/claude/skills/target-setup/references/detect.md +3 -3
  55. package/claude/skills/test-craft/SKILL.md +2 -2
  56. package/claude/skills/ux-audit/REQUIREMENT.md +1 -1
  57. package/claude/skills/ux-audit/SKILL.md +2 -2
  58. package/claude/skills/ux-measure/REQUIREMENT.md +1 -1
  59. package/claude/skills/write-human/references/machine-tells.md +12 -0
  60. package/docs/agents/audits.md +1 -1
  61. package/docs/agents/commands.md +14 -13
  62. package/docs/agents/comments.md +1 -1
  63. package/docs/agents/counts.md +5 -6
  64. package/docs/agents/drift-surfaces.md +15 -19
  65. package/docs/agents/gov-detect.md +1 -1
  66. package/docs/agents/index.md +2 -3
  67. package/docs/agents/install-and-sync.md +10 -34
  68. package/docs/agents/markdown-audit.md +22 -12
  69. package/docs/agents/migrate.md +1 -1
  70. package/docs/agents/output-shape.md +1 -1
  71. package/docs/agents/overview.md +2 -2
  72. package/docs/agents/records-stale.md +1 -1
  73. package/docs/agents/records-validate.md +11 -23
  74. package/docs/agents/records.md +1 -1
  75. package/docs/agents/routing.md +1 -1
  76. package/docs/agents/rule-citations.md +9 -17
  77. package/docs/agents/scripting.md +2 -20
  78. package/docs/agents/state-scoped-risk.md +1 -1
  79. package/docs/agents/tasks-validate.md +1 -1
  80. package/docs/agents/teach.md +1 -1
  81. package/docs/target/index.md +1 -0
  82. package/docs/target/migrations.md +4 -0
  83. package/docs/target/projects.md +10 -10
  84. package/docs/target/scenarios.md +5 -6
  85. package/docs/target/standards-moved.md +32 -0
  86. package/docs/target/sync.md +5 -5
  87. package/docs/workflow/ai-workflow.md +1 -1
  88. package/docs/workflow/operating-model.md +16 -18
  89. package/docs/workflow/skill-map.md +1 -1
  90. package/docs/workflow/visual-design-workflow.md +3 -3
  91. package/governance/detect.toml +32 -36
  92. package/governance/renames.toml +44 -1
  93. package/governance/rules/lib/{305-e2e-reliability.md → e2e-reliability.md} +1 -1
  94. package/governance/rules/lib/{306-test-scope.md → test-scope.md} +2 -2
  95. package/governance/rules/ui/{410-a11y.md → a11y.md} +1 -1
  96. package/governance/rules/ui/{440-surface-capture.md → surface-capture.md} +3 -3
  97. package/governance/stacks/base.toml +15 -8
  98. package/governance/stacks/go.toml +2 -0
  99. package/governance/stacks/node.toml +1 -1
  100. package/governance/stacks/php.toml +2 -0
  101. package/governance/stacks/python.toml +1 -1
  102. package/package.json +1 -4
  103. package/scripts/core/install-check.sh +4 -10
  104. package/src/audits/catalog.ts +0 -9
  105. package/src/capture/frame-values.ts +3 -6
  106. package/src/claude/routing.ts +1 -1
  107. package/src/claude/skills-reach.ts +4 -6
  108. package/src/cli/help-all.ts +96 -0
  109. package/src/cli/help.ts +29 -29
  110. package/src/cli.ts +8 -3
  111. package/src/commands/claude.ts +29 -0
  112. package/src/commands/design.ts +22 -1
  113. package/src/commands/gov.ts +29 -11
  114. package/src/commands/init.ts +10 -1
  115. package/src/commands/labels.ts +4 -44
  116. package/src/commands/markdown.ts +96 -3
  117. package/src/commands/migrate.ts +1 -1
  118. package/src/commands/records.ts +5 -21
  119. package/src/commands/sync.ts +11 -0
  120. package/src/commands/tooling.ts +10 -4
  121. package/src/counts/catalogs.ts +0 -6
  122. package/src/design/budget.ts +14 -10
  123. package/src/design/document.ts +5 -5
  124. package/src/design/parse.ts +21 -4
  125. package/src/design/render.ts +3 -3
  126. package/src/gate/boundaries.ts +2 -2
  127. package/src/gate/measures.ts +7 -29
  128. package/src/gate/sequencer.ts +51 -2
  129. package/src/gate/stages.ts +0 -18
  130. package/src/gov/citations.ts +16 -71
  131. package/src/gov/stacks.ts +3 -3
  132. package/src/inventory/subjects.ts +1 -1
  133. package/src/markdown/bans.ts +1 -1
  134. package/src/markdown/ceiling.ts +6 -1
  135. package/src/markdown/colons.ts +86 -0
  136. package/src/markdown/gate.ts +15 -0
  137. package/src/markdown/heading-case.ts +74 -0
  138. package/src/migrate/scratch-evidence.ts +2 -2
  139. package/src/records/validate.ts +6 -194
  140. package/src/sandbox/provision.ts +0 -5
  141. package/src/secrets/shipped.ts +1 -2
  142. package/src/serve/static.ts +1 -2
  143. package/src/shipped/references.ts +42 -93
  144. package/src/sync/check.ts +18 -14
  145. package/src/sync/copy.ts +4 -5
  146. package/src/tooling/inject.ts +6 -37
  147. package/src/tooling/read.ts +2 -3
  148. package/src/tooling/scan.ts +4 -17
  149. package/src/tooling/subfolder.ts +2 -66
  150. package/tooling/astro/manifest.toml +0 -1
  151. package/tooling/astro/reference.md +0 -8
  152. package/tooling/base/configs/scripts/verify.json +1 -1
  153. package/tooling/base/configs/scripts/verify.ts +22 -17
  154. package/tooling/base/manifest.toml +7 -7
  155. package/tooling/base/reference.md +19 -32
  156. package/tooling/base/seeds/.github/workflows/verify.yml +0 -9
  157. package/tooling/base/seeds/.lintstagedrc +2 -4
  158. package/tooling/base/seeds/canon/context/ci.md +4 -6
  159. package/tooling/base/seeds/canon/context/development.md +2 -2
  160. package/tooling/claude/reference.md +1 -1
  161. package/tooling/claude/seeds/.claude/hooks/review-post-scan.sh +13 -1
  162. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +1 -1
  163. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +27 -2
  164. package/tooling/claude/seeds/CLAUDE.md +1 -1
  165. package/tooling/claude/seeds/canon/DESIGN.md +24 -37
  166. package/tooling/go/reference.md +1 -1
  167. package/tooling/php/reference.md +1 -1
  168. package/tooling/python/reference.md +2 -2
  169. package/tooling/shell/configs/.github/workflows/shell.yml +30 -0
  170. package/tooling/shell/manifest.toml +12 -0
  171. package/tooling/shell/reference.md +46 -0
  172. package/tooling/vite-react/manifest.toml +0 -1
  173. package/tooling/vite-react/reference.md +0 -8
  174. package/tooling/web/configs/.github/workflows/readme-screenshot.yml +3 -5
  175. package/tooling/web/configs/.github/workflows/verify.yml +0 -9
  176. package/tooling/web/manifest.toml +1 -2
  177. package/tooling/web/reference.md +4 -6
  178. package/docs/agents/standards-audit.md +0 -40
  179. package/governance/rules/tooling/765-spelling.md +0 -19
  180. package/governance/stacks/astro.toml +0 -2
  181. package/governance/stacks/nextjs.toml +0 -2
  182. package/governance/stacks/node-server.toml +0 -2
  183. package/governance/stacks/python-fastapi.toml +0 -2
  184. package/governance/stacks/react.toml +0 -2
  185. package/src/commands/standards.ts +0 -278
  186. package/src/labels/spelling.ts +0 -120
  187. package/src/standards/audit.ts +0 -146
  188. package/src/standards/list.ts +0 -33
  189. package/src/standards/read.ts +0 -177
  190. package/src/standards/scope.ts +0 -56
  191. package/standards/index.md +0 -10
  192. package/tooling/base/configs/.vscode/extensions.json +0 -9
  193. package/tooling/base/configs/.vscode/settings.json +0 -3
  194. package/tooling/base/configs/scripts/verify.sh +0 -2
  195. package/tooling/base/seeds/.cspell/project-terms.txt +0 -0
  196. package/tooling/base/seeds/.cspell/tech-stack.txt +0 -25
  197. package/tooling/base/seeds/cspell.json +0 -20
  198. package/tooling/go/seeds/.cspell/tech-stack.txt +0 -16
  199. package/tooling/nextjs/seeds/.cspell/tech-stack.txt +0 -3
  200. package/tooling/php/seeds/.cspell/tech-stack.txt +0 -5
  201. package/tooling/python/seeds/.cspell/tech-stack.txt +0 -18
  202. package/tooling/vite-react/seeds/.cspell/project-terms.txt +0 -1
  203. package/tooling/vite-react/seeds/.cspell/tech-stack.txt +0 -2
  204. package/tooling/web/configs/.vscode/extensions.json +0 -13
  205. package/tooling/web/configs/.vscode/settings.json +0 -10
  206. package/tooling/web/seeds/.cspell/tech-stack.txt +0 -21
  207. /package/governance/rules/canon/{606-git.md → git.md} +0 -0
  208. /package/governance/rules/canon/{611-indexes.md → indexes.md} +0 -0
  209. /package/governance/rules/canon/{604-scratch.md → scratch.md} +0 -0
  210. /package/governance/rules/canon/{602-tasks.md → task-scope.md} +0 -0
  211. /package/governance/rules/canon/{605-worktrees.md → worktrees.md} +0 -0
  212. /package/governance/rules/claude/{566-output.md → output.md} +0 -0
  213. /package/governance/rules/code/{000-code.md → routing.md} +0 -0
  214. /package/governance/rules/code/{010-testing.md → testing.md} +0 -0
  215. /package/governance/rules/framework/{210-astro.md → astro.md} +0 -0
  216. /package/governance/rules/framework/{220-fastapi.md → fastapi.md} +0 -0
  217. /package/governance/rules/framework/{230-nextjs.md → nextjs.md} +0 -0
  218. /package/governance/rules/framework/{200-react.md → react.md} +0 -0
  219. /package/governance/rules/framework/{260-shadcn.md → shadcn.md} +0 -0
  220. /package/governance/rules/framework/{250-tailwind.md → tailwind.md} +0 -0
  221. /package/governance/rules/lang/{130-go.md → go.md} +0 -0
  222. /package/governance/rules/lang/{140-php.md → php.md} +0 -0
  223. /package/governance/rules/lang/{110-python.md → python.md} +0 -0
  224. /package/governance/rules/lang/{100-typescript.md → typescript.md} +0 -0
  225. /package/governance/rules/lib/{370-database.md → database.md} +0 -0
  226. /package/governance/rules/lib/{340-pydantic.md → pydantic.md} +0 -0
  227. /package/governance/rules/lib/{360-security-server.md → security-server.md} +0 -0
  228. /package/governance/rules/lib/{350-security-web.md → security-web.md} +0 -0
  229. /package/governance/rules/lib/{320-tanstack-query.md → tanstack-query.md} +0 -0
  230. /package/governance/rules/lib/{335-testing-go.md → testing-go.md} +0 -0
  231. /package/governance/rules/lib/{336-testing-php.md → testing-php.md} +0 -0
  232. /package/governance/rules/lib/{330-testing-py.md → testing-py.md} +0 -0
  233. /package/governance/rules/lib/{300-testing-ts.md → testing-ts.md} +0 -0
  234. /package/governance/rules/lib/{310-zod.md → zod.md} +0 -0
  235. /package/governance/rules/tooling/{700-ci-workflow.md → ci-workflow.md} +0 -0
  236. /package/governance/rules/tooling/{790-code-comments.md → code-comments.md} +0 -0
  237. /package/governance/rules/tooling/{780-config-comments.md → config-comments.md} +0 -0
  238. /package/governance/rules/tooling/{775-dependencies.md → dependencies.md} +0 -0
  239. /package/governance/rules/ui/{480-canvas.md → canvas.md} +0 -0
  240. /package/governance/rules/ui/{400-ui.md → copy.md} +0 -0
  241. /package/governance/rules/ui/{460-design-taste.md → design-taste.md} +0 -0
  242. /package/governance/rules/ui/{420-forms.md → forms.md} +0 -0
  243. /package/governance/rules/ui/{465-interface-casing.md → interface-casing.md} +0 -0
  244. /package/governance/rules/ui/{450-link-behavior.md → link-behavior.md} +0 -0
  245. /package/governance/rules/ui/{470-motion.md → motion.md} +0 -0
  246. /package/governance/rules/ui/{430-ux-completeness.md → ux-completeness.md} +0 -0
  247. /package/tooling/{base → shell}/configs/.editorconfig +0 -0
  248. /package/tooling/{base → shell}/configs/.shellcheckrc +0 -0
@@ -62,13 +62,28 @@ fi
62
62
  hits=$(printf '%s' "$record" |
63
63
  jq -r '.entries[]?.bans[]? | ":\(.line):\(.column + 1) \(.kind) \(.term)"' 2>/dev/null)
64
64
 
65
+ # Two advisory measures ride in the same record and never gate. A heading's
66
+ # case is read for the whole file, since it is cheap to fix where it stands. A
67
+ # colon is kept only where the word before it and the colon appear in the text
68
+ # this write carried, since an edit often rewrites part of a line and a
69
+ # whole-line match would drop exactly that case. The verb names that word as
70
+ # `lead`, since slicing `text` at `column` miscounts after an emoji, and a
71
+ # binary predating the field falls back to the slice. A file holds colons nobody is
72
+ # asking this edit to answer for.
73
+ new=$(printf '%s' "$input" |
74
+ jq -r '[.tool_input.new_string?, .tool_input.content?, (.tool_input.edits[]?.new_string?)] | map(select(. != null)) | join("\n")' 2>/dev/null)
75
+ headings=$(printf '%s' "$record" |
76
+ jq -r '.entries[]?.headingCase[]? | ":\(.line) \(.text)"' 2>/dev/null)
77
+ colons=$(printf '%s' "$record" |
78
+ jq -r --arg new "$new" '.entries[]?.colons[]? | (.lead // (.text[:.column] | sub("^.*\\s"; ""))) as $lead | select($new | contains($lead + ":")) | ":\(.line):\(.column + 1) \(.text)"' 2>/dev/null)
79
+
65
80
  # A set the verb shipped empty measures nothing and would report a clean file.
66
81
  # Reading the findings alone turns that narrowed check into a pass, so the field
67
82
  # is read beside them.
68
83
  empty=$(printf '%s' "$record" |
69
84
  jq -r '[.bans.emptySets[]?] | join(", ")' 2>/dev/null)
70
85
 
71
- [ -z "$hits" ] && [ -z "$unread" ] && [ -z "$empty" ] && exit 0
86
+ [ -z "$hits" ] && [ -z "$headings" ] && [ -z "$colons" ] && [ -z "$unread" ] && [ -z "$empty" ] && exit 0
72
87
 
73
88
  nl=$'\n'
74
89
  msg=""
@@ -82,7 +97,17 @@ elif [ -n "$empty" ]; then
82
97
  fi
83
98
 
84
99
  if [ -n "$hits" ]; then
85
- found=$(printf 'Standards-audit: markdown.md violations in %s. Rewrite the sentence (do not lazy-swap). A code span is the answer only where the token is genuinely an identifier under discussion.\n%s' "$file" "$hits")
100
+ found=$(printf 'Standards-audit: markdown.md violations in %s. Split the sentence or rewrite it. A colon or a comma standing where the em dash was is the same sentence. A code span is the answer only where the token is genuinely an identifier under discussion.\n%s' "$file" "$hits")
101
+ msg="${msg:+$msg$nl}$found"
102
+ fi
103
+
104
+ if [ -n "$headings" ]; then
105
+ found=$(printf 'Standards-audit: headings in %s are not sentence case. Lowercase every word after the first unless it is a proper noun, and leave a proper noun as it is.\n%s' "$file" "$headings")
106
+ msg="${msg:+$msg$nl}$found"
107
+ fi
108
+
109
+ if [ -n "$colons" ]; then
110
+ found=$(printf 'Standards-audit: this write added a colon in the middle of a sentence in %s. A colon belongs after a label or at the end of a line introducing a list. Split the sentence or rewrite it.\n%s' "$file" "$colons")
86
111
  msg="${msg:+$msg$nl}$found"
87
112
  fi
88
113
 
@@ -16,4 +16,4 @@
16
16
  ## Key paths
17
17
 
18
18
  - `src/`: [description]
19
- - `canon/DESIGN.md`: design tokens and the visual system
19
+ - `canon/DESIGN.md`: visual intent, pointing at the stylesheet that holds the values
@@ -1,57 +1,44 @@
1
1
  # Design
2
2
 
3
- Authoring guidance: the design standard.
3
+ Authoring guidance: the design standard. Values live in `<your stylesheet>`, such as `src/styles/tokens.css` or a Tailwind `@theme` block. A project with none writes them under `.claude/design/project/`, beside the installed base stylesheet.
4
4
 
5
- ## Personality
5
+ ## Overview
6
6
 
7
7
  One paragraph. Voice, tone, and the feeling a user should have. The same content a design intake form asks for.
8
8
 
9
- ## Color
9
+ ## Colors
10
10
 
11
- One row per role. Intent is a short phrase a human can picture. Value is a hex or a named system token.
11
+ Up to three rules a developer could get wrong.
12
12
 
13
- | Role | Intent | Value |
14
- | ---------- | ----------------------------- | ----- |
15
- | background | page canvas | |
16
- | surface | cards, panels, raised blocks | |
17
- | text | primary body text | |
18
- | muted | secondary text, captions | |
19
- | accent | links, primary action | |
20
- | success | confirmations, positive state | |
21
- | warning | cautions, pending state | |
22
- | error | failures, destructive action | |
13
+ One row per role, naming its custom property. Intent is a short phrase a human can picture.
23
14
 
24
- ## Typography
15
+ | Role | Property | Intent |
16
+ | ---------- | -------------------- | ----------------------------- |
17
+ | background | `--color-background` | page canvas |
18
+ | surface | `--color-surface` | cards, panels, raised blocks |
19
+ | text | `--color-text` | primary body text |
20
+ | muted | `--color-muted` | secondary text, captions |
21
+ | accent | `--color-accent` | links, primary action |
22
+ | success | `--color-success` | confirmations, positive state |
23
+ | warning | `--color-warning` | cautions, pending state |
24
+ | error | `--color-error` | failures, destructive action |
25
25
 
26
- One row per role. Size and line height in pixels or rem. Family names use their product casing.
26
+ ## Typography
27
27
 
28
- | Role | Family | Weight | Size | Line height |
29
- | ------- | ------ | ------ | ---- | ----------- |
30
- | display | | | | |
31
- | heading | | | | |
32
- | body | | | | |
33
- | label | | | | |
34
- | code | | | | |
28
+ Up to three rules. How the roles divide between families, and any role that breaks the pattern.
35
29
 
36
30
  ## Spacing
37
31
 
38
- Base unit and scale. The renderer draws a swatch per step.
32
+ One line. The base unit and how the scale steps.
39
33
 
40
- | Step | Multiplier | Value |
41
- | ---- | ---------- | ----- |
42
- | xs | 0.5 | |
43
- | sm | 1 | |
44
- | md | 2 | |
45
- | lg | 3 | |
46
- | xl | 5 | |
34
+ ## Shapes
47
35
 
48
- ## Borders
36
+ One row per role, naming its radius property.
49
37
 
50
- | Role | Radius | Width | When used |
51
- | ------- | ------ | ----- | --------------------- |
52
- | default | | | cards, inputs |
53
- | pill | | | tags, status chips |
54
- | none | 0 | 0 | edge-to-edge surfaces |
38
+ | Role | Property | When used |
39
+ | ------- | ------------------ | ------------------ |
40
+ | default | `--radius-default` | cards, inputs |
41
+ | pill | `--radius-pill` | tags, status chips |
55
42
 
56
43
  ## Motion
57
44
 
@@ -10,7 +10,7 @@ The go stack covers Go modules linted and formatted by `golangci-lint` v2, type-
10
10
 
11
11
  1. Scaffold with `go mod init <module-path>`. The module path is the project's own identity, so this stack never ships a `go.mod`.
12
12
  2. Keep `./...` out of `node_modules`: `go mod edit -ignore=./node_modules`. At a root sync the base layer installs `node_modules/` inside the module, and a package there can ship a `.go` file that `go vet`, `go test`, and `golangci-lint` would otherwise walk. The directive needs Go 1.25 or later.
13
- 3. Seed `package.json` so the base layer's bun-side tools (husky, prettier, cspell, commitlint) have a target to install into: `bun init -y`.
13
+ 3. Seed `package.json` so the base layer's bun-side tools (husky, prettier, commitlint) have a target to install into: `bun init -y`.
14
14
  4. Install base tooling: `canon tooling sync base . --write`
15
15
  5. Install go tooling: `canon tooling sync go . --write`
16
16
  6. Install the linter: `go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest`. The manifest declares no `[dependencies.dev]`, because manifest injection runs `bun add -D`, which cannot install a Go tool. This step stays manual.
@@ -9,7 +9,7 @@ The php stack covers Composer projects on PHP 8.3 or later, formatted by PHP CS
9
9
  ## Scaffold checklist
10
10
 
11
11
  1. Scaffold with `composer init -n --name <vendor>/<name> --autoload src/`. The package name and namespace are the project's own identity, so this stack never ships a `composer.json`.
12
- 2. Seed `package.json` so the base layer's bun-side tools (husky, prettier, cspell, commitlint) have a target to install into: `bun init -y`.
12
+ 2. Seed `package.json` so the base layer's bun-side tools (husky, prettier, commitlint) have a target to install into: `bun init -y`.
13
13
  3. Install base tooling: `canon tooling sync base . --write`
14
14
  4. Install php tooling: `canon tooling sync php . --write`
15
15
  5. Install the dev tools: `composer require --dev friendsofphp/php-cs-fixer phpstan/phpstan phpunit/phpunit`. The manifest declares no `[dependencies.dev]`, because manifest injection runs `bun add -D`, which cannot install a Composer package. This step stays manual.
@@ -11,7 +11,7 @@ Configs ship as sidecar files (`ruff.toml`, `mypy.ini`, `pytest.ini`, `.coverage
11
11
  ## Scaffold checklist
12
12
 
13
13
  1. Scaffold with `uv init --package <name>`. This creates `pyproject.toml` with a `uv_build` backend, `.python-version` pinned to 3.14, and `src/<name with underscores>/__init__.py` holding an annotated `def main() -> None:`, so source lands under `src/` where `mypy.ini`, `pytest.ini`, and `.coveragerc` look for it. `uv init` defaults `requires-python` to `>=3.14`, which matches the `.python-version` pin this stack ships.
14
- 2. Seed `package.json` so the base layer's bun-side tools (husky, prettier, cspell, commitlint) have a target to install into: `bun init -y`. Without this the sync drops base configs but skips the dep install, since `resolve_missing_deps` short-circuits when `package.json` is absent.
14
+ 2. Seed `package.json` so the base layer's bun-side tools (husky, prettier, commitlint) have a target to install into: `bun init -y`. Without this the sync drops base configs but skips the dep install, since `resolve_missing_deps` short-circuits when `package.json` is absent.
15
15
  3. Install base tooling: `canon tooling sync base . --write`
16
16
  4. Install python tooling: `canon tooling sync python . --write`
17
17
  5. Install Python tooling deps: `uv add --dev ruff mypy pytest pytest-cov`. v1 of this stack does not declare these in `[dependencies.dev]` because manifest injection hardcodes `bun add -D`, which can not install Python packages. Until the injector branches on `runtime`, this step is manual.
@@ -31,7 +31,7 @@ The python stack also ships `tests/test_smoke.py` as a copy-once seed. `pytest`
31
31
 
32
32
  ## Hybrid project shape
33
33
 
34
- A python project synced with this stack ends up with both `package.json` from base and `pyproject.toml` from `uv init`. The base layer brings prettier, cspell, commitlint, and husky for non-Python files. Both `node_modules/` and `.venv/` coexist at the project root. The hybrid shape gives Python projects access to the toolkit's cross-cutting tools without forking the base stack.
34
+ A python project synced with this stack ends up with both `package.json` from base and `pyproject.toml` from `uv init`. The base layer brings prettier, commitlint, and husky for non-Python files. Both `node_modules/` and `.venv/` coexist at the project root. The hybrid shape gives Python projects access to the toolkit's cross-cutting tools without forking the base stack.
35
35
 
36
36
  ## Dependencies
37
37
 
@@ -0,0 +1,30 @@
1
+ name: Shell
2
+ # canon-no-seed: an add-on stack's workflow with no root counterpart by design, since the toolkit checks its own shell inside verify.yml.
3
+
4
+ on:
5
+ pull_request:
6
+ branches:
7
+ - main
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ shell-checks:
12
+ name: 🐚 Shell Checks
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Checkout
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Setup Bun
19
+ uses: oven-sh/setup-bun@v2
20
+ with:
21
+ bun-version: latest
22
+
23
+ - name: Install Dependencies
24
+ run: bun install --frozen-lockfile
25
+
26
+ - name: Install Shell Tools
27
+ run: sudo apt-get install -y shfmt shellcheck
28
+
29
+ - name: Check Shell
30
+ run: bun run check:shell
@@ -0,0 +1,12 @@
1
+ [stack]
2
+ name = "shell"
3
+ extends = ""
4
+ runtime = "bun"
5
+ scaffold = ""
6
+
7
+ [sync]
8
+ source = "configs"
9
+
10
+ [scripts]
11
+ "check:shell" = "find scripts -name '*.sh' -exec shfmt --diff --indent 2 {} + && find scripts -name '*.sh' -exec shellcheck --severity=warning {} +"
12
+ "format:shell" = "find scripts -name '*.sh' -exec shfmt --write --indent 2 {} +"
@@ -0,0 +1,46 @@
1
+ # Tooling shell reference
2
+
3
+ ## Overview
4
+
5
+ The shell stack is an add-on that formats and lints the shell scripts a project writes. It extends nothing and syncs beside any other stack at the same root with `canon tooling sync shell . --write`, so a project that writes no shell never installs shfmt or shellcheck.
6
+
7
+ ## Install
8
+
9
+ - Install `shfmt` and `shellcheck` by hand, through `sudo apt-get install shfmt shellcheck` or `brew install shfmt shellcheck`. Injection runs only `bun add -D`, and neither tool ships through the registry.
10
+ - `scripts/verify.ts` probes both before the `Shell` phase and names the install command when one is missing, rather than letting `find` report a missing executable.
11
+
12
+ ## shfmt
13
+
14
+ - Indent with 2 spaces, set through `--indent 2` on every call.
15
+ - `.editorconfig` carries an `[*.sh]` block with `indent_style = space` and `indent_size = 2`, so the editor and shfmt agree. Dropping the block because shfmt already sets indentation produces spurious diffs.
16
+
17
+ ## shellcheck
18
+
19
+ - Run at warning severity.
20
+ - `.shellcheckrc` sets `external-sources=true`, which shellcheck needs to follow `source` directives.
21
+
22
+ ## Coverage
23
+
24
+ - Both tools take their files from `find scripts -name '*.sh'`, since `scripts/` can mix shell with TypeScript and shellcheck has no directory mode.
25
+ - A shell script with a shebang and no `.sh` extension is covered by neither, so give it the extension.
26
+ - Keep shell scripts in `scripts/`. A `.sh` file elsewhere, such as a seeded `.claude/hooks/` script, is left out on purpose.
27
+
28
+ ## Verify
29
+
30
+ - `scripts/verify.ts` runs `check:shell` in its `Shell` phase wherever `package.json` declares it, between the format check and the unit tests. No stack's `scripts/verify.json` requires it, so a folder that never synced this stack skips the phase.
31
+
32
+ ## lint-staged
33
+
34
+ - `.lintstagedrc` is a base seed this stack cannot reach once written. Add a `**/*.sh` entry by hand, running `shfmt --write --indent 2` then `shellcheck --severity=warning`, to run the checks on commit.
35
+ - Do NOT copy that glob into a `package.json` script. lint-staged expands its own globs and passes matched files as arguments, which a bare shell script does not.
36
+
37
+ ## CI
38
+
39
+ - `.github/workflows/shell.yml` runs on pull requests targeting `main` and on `workflow_dispatch`: checkout, setup Bun, `bun install --frozen-lockfile`, install both tools through apt, then `check:shell`.
40
+
41
+ ## CLI
42
+
43
+ | Script | What it does |
44
+ | ---------------------- | --------------------------------------------------------------- |
45
+ | `bun run check:shell` | Asserts shfmt formatting, then runs shellcheck at warning level |
46
+ | `bun run format:shell` | Writes shfmt formatting in place |
@@ -15,7 +15,6 @@ packages = [
15
15
  "build" = "tsc --noEmit && vite build"
16
16
  "preview" = "WORKTREE_PORT_OFFSET=$(bash scripts/worktree-port.sh) && export WORKTREE_PORT_OFFSET && vite preview"
17
17
  "typecheck" = "tsc --noEmit"
18
- "setup" = "./scripts/setup.sh"
19
18
 
20
19
  [scripts.override]
21
20
  "build" = "tsc --noEmit && vite build"
@@ -33,14 +33,6 @@ When scaffolding a Chrome extension, override the installed golden configs:
33
33
  - `screenshot` script: `bun run build && bun e2e/screenshot.ts` (no preview server).
34
34
  - Additional gitignore entries: `*.crx`, `*.pem`, `release/`.
35
35
 
36
- ## Setup script
37
-
38
- - File: `scripts/setup.sh`. Destructive: deletes `.git` and self-removes after running. Run once immediately after scaffolding.
39
- - Prompt for project name, normalize to kebab-case, derive title-cased display name.
40
- - Update `package.json` name and version, inject verify/clean/update scripts, remove setup.
41
- - Update `<title>` tags in HTML files.
42
- - Wipe `.git`, re-init, make scripts executable, commit as `chore(root): initialize <name>`.
43
-
44
36
  ## Development docs (extend)
45
37
 
46
38
  Append to the `## Scripts` table:
@@ -95,11 +95,9 @@ jobs:
95
95
  # and stops here without pushing a second time.
96
96
  if: steps.diff.outputs.changed == 'true'
97
97
  # The hooks are off because this commit carries nothing they can read.
98
- # `pre-push` runs the full verify, whose format step shells out to
99
- # `shfmt`, which the runner does not install, so a push through them
100
- # exits 127. The jobs in verify.yml already run those checks against
101
- # the source, and what this step pushes is PNG bytes a capture
102
- # produced.
98
+ # `pre-push` runs the full verify, which reruns every check the jobs
99
+ # in verify.yml already ran against the source, and what this step
100
+ # pushes is PNG bytes a capture produced.
103
101
  env:
104
102
  HUSKY: 0
105
103
  run: |
@@ -30,9 +30,6 @@ jobs:
30
30
  - name: Install Dependencies
31
31
  run: bun install --frozen-lockfile
32
32
 
33
- - name: Install Shell Tools
34
- run: sudo apt-get install -y shfmt shellcheck
35
-
36
33
  - name: Typecheck
37
34
  run: bun run typecheck
38
35
 
@@ -42,12 +39,6 @@ jobs:
42
39
  - name: Check Formatting
43
40
  run: bun run check:format
44
41
 
45
- - name: Check Spelling
46
- run: bun run check:spell
47
-
48
- - name: Check Shell
49
- run: bun run check:shell
50
-
51
42
  unit-tests:
52
43
  name: 🧪 Unit Tests
53
44
  runs-on: ubuntu-latest
@@ -46,7 +46,7 @@ packages = [
46
46
  "test:e2e:changed" = "WORKTREE_PORT_OFFSET=$(bash scripts/worktree-port.sh) && export WORKTREE_PORT_OFFSET && playwright test --only-changed"
47
47
  "test:e2e:ui" = "WORKTREE_PORT_OFFSET=$(bash scripts/worktree-port.sh) && export WORKTREE_PORT_OFFSET && playwright test --ui"
48
48
  "test:e2e:report" = "playwright show-report"
49
- "check:full" = "./scripts/verify.sh && bun run test:e2e"
49
+ "check:full" = "bun scripts/verify.ts && bun run test:e2e"
50
50
  "smoke:prod" = "bun e2e/screenshot.ts --check-console-clean --require-base-url"
51
51
 
52
52
  [scripts.override]
@@ -59,4 +59,3 @@ packages = [
59
59
  "# Coverage" = ["coverage/"]
60
60
  "# Playwright" = ["test-results/", "playwright-report/", "blob-report/", "playwright/.cache/"]
61
61
  "# Screenshots" = ["screenshots/"]
62
- "# VSCode" = [".vscode/*", "!.vscode/extensions.json", "!.vscode/settings.json"]
@@ -16,8 +16,7 @@ Golden config files live in `tooling/web/configs/` and are copied into the targe
16
16
  - `scripts/screenshot.sh`: sources `scripts/lib/preview-server.sh`, then runs the capture against the served preview.
17
17
  - `scripts/lib/preview-server.sh`: sourced, never executed. Builds, serves on `PREVIEW_PORT`, blocks until the port answers, and traps the whole process group on exit, including the detached grandchild `lsof` finds. Both capture scripts source it so the startup lives once.
18
18
  - `scripts/readme-screenshot.sh`: captures one case under `SCREENSHOT_FILTER` and copies its light and dark frames to `assets/evidence/readme/`. `README_FILTER` ships empty with `header/1280` as its example, and the script exits 1 until a project fills it, since a shipped default would capture the wrong part of the page in every project but one. Its copy reads from `screenshots/<hostname>/`, which is where the harness writes.
19
- - `.github/workflows/readme-screenshot.yml`: refreshes that frame on a pull request and commits it back to the branch. The path list ships as `REPLACE_WITH_PROJECT_PATHS`, and the job's first step after checkout exits 1 while it stays, since a job that never fires leaves no run and no diff to notice. The workflow's own path is in the list so the installing pull request reaches that step. When a commit-back is right and when a report is, `governance/rules/tooling/700-ci-workflow.md` states the test.
20
- - `.vscode/extensions.json` and `.vscode/settings.json`: editor wiring for ESLint, Tailwind, Playwright, Vitest.
19
+ - `.github/workflows/readme-screenshot.yml`: refreshes that frame on a pull request and commits it back to the branch. The path list ships as `REPLACE_WITH_PROJECT_PATHS`, and the job's first step after checkout exits 1 while it stays, since a job that never fires leaves no run and no diff to notice. The workflow's own path is in the list so the installing pull request reaches that step. When a commit-back is right and when a report is, `governance/rules/tooling/ci-workflow.md` states the test.
21
20
  - `.github/workflows/verify.yml`: `static-checks`, `unit-tests`, `build-verify`, and `e2e-tests` jobs.
22
21
  - `scripts/verify.json`: the phases base's verify runner requires here, being typecheck, lint, unit tests, and build. `## Verify script` covers the order and the card exclusion stage.
23
22
  - `scripts/worktree-port.sh`: prints a base port plus this working directory's offset. Called with no argument it prints the offset alone. It refuses a folder left under the worktrees directory after its worktree was removed, rather than printing a port for it.
@@ -78,9 +77,8 @@ Sticky negative knowledge. Do not relearn.
78
77
 
79
78
  ## lint-staged (extend)
80
79
 
81
- - Add `**/*.{js,jsx,ts,tsx}` glob running `eslint --fix --max-warnings 0`, `prettier --write`, `cspell --no-must-find-files`.
80
+ - Add `**/*.{js,jsx,ts,tsx}` glob running `eslint --fix --max-warnings 0`, and `prettier --write`.
82
81
  - Extend the prettier glob to include `css`: `**/*.{json,css,md,mdc}`.
83
- - Each file type runs cspell once via its own glob. No standalone cspell glob.
84
82
 
85
83
  ## CI docs (extend)
86
84
 
@@ -115,7 +113,7 @@ Append rows:
115
113
 
116
114
  `canon tooling verify <stack>` is the only automated caller of `bun run screenshot`, running it for any stack whose `package.json` declares the script and asserting that PNG files land under `screenshots/`. It counts them with a recursive find carrying no depth limit, so the section folders the seed writes satisfy the assertion without a change to it. Do not flatten the layout to protect that check. No ship chain captures a screenshot, so the output path the seed writes is a contract that one verifier reads rather than a default a ship step depends on.
117
115
 
118
- `governance/rules/ui/440-surface-capture.md` is what asks a session to run the capture after a surface changes. It now fires on every component file too, reversing the route-and-page-only scope this reference once described, after a shipped batch of components carried no capture and a defect went unseen. The rule body states its own exemption for a component the production build strips out, so a reader chasing that case reads it there rather than here.
116
+ `governance/rules/ui/surface-capture.md` is what asks a session to run the capture after a surface changes. It now fires on every component file too, reversing the route-and-page-only scope this reference once described, after a shipped batch of components carried no capture and a defect went unseen. The rule body states its own exemption for a component the production build strips out, so a reader chasing that case reads it there rather than here.
119
117
 
120
118
  The sweep under `screenshots/` is ignored again, and only a flagged case's `evidence/` output tracks in git, so the first capture a scaffolded target runs after this change is the baseline it commits there. A case naming sections commits a folder per section under that baseline rather than one file per theme, which is what lets a pull request comparison land on the part of the page that moved instead of on one image of the whole of it. The count is the section list times the themes, so a list is worth sizing against how many frames each later change has to regenerate and a reviewer has to read.
121
119
 
@@ -128,6 +126,6 @@ The sweep under `screenshots/` is ignored again, and only a flagged case's `evid
128
126
 
129
127
  ## Verify script
130
128
 
131
- The web layer's `scripts/verify.json` requires `typecheck`, `lint`, `test:run`, and `build`, and base's `scripts/verify.ts` runs them in its fixed order: typecheck, lint, format, spelling, shell, unit tests, build, markdown bans, then the card exclusion check where one is installed. astro, vite-react, and nextjs inherit the list, since the nearest stack's copy wins. An adapter whose phases differ ships its own `verify.json` rather than a script.
129
+ The web layer's `scripts/verify.json` requires `typecheck`, `lint`, `test:run`, and `build`, and base's `scripts/verify.ts` runs them in its fixed order: typecheck, lint, format, shell where the `shell` stack declares it, unit tests, build, markdown bans, then the card exclusion check where one is installed. astro, vite-react, and nextjs inherit the list, since the nearest stack's copy wins. An adapter whose phases differ ships its own `verify.json` rather than a script.
132
130
 
133
131
  The card stage runs `scripts/check-card-exclusion.sh` whenever that file is installed, which keeps the social card route's build exclusion on the default path rather than behind a flag. It tests for the script rather than the stack, so a project with no card route runs one less stage with no override, and the stack shipping a card route owns what the check reads, being the build the stage before it produced. The astro reference carries the route itself.
@@ -1,40 +0,0 @@
1
- ---
2
- title: Standard success criteria
3
- description: Reading the corpus against the success-criterion gate, the two headings it accepts, why the check scopes to arrival rather than the whole corpus, and the exit codes it sets
4
- ---
5
-
6
- # Standard success criteria
7
-
8
- `canon standards audit` reads the corpus at `standards/` and reports which files state a success criterion against which do not, per `standards/standard.md`. It fails only on a standard new to the current branch, never on one already short the section.
9
-
10
- Two headings count, at any casing. `## Success criterion` is the wording that standard states the rule under, and `## What a working <document type> looks like` is the wording its template prescribes for the section itself. Most of the corpus writes the templated form, so accepting the rule's wording alone would refuse a conforming standard.
11
-
12
- ```bash
13
- canon standards audit
14
- canon standards audit --json
15
- canon standards audit --arrivals-only
16
- ```
17
-
18
- | Option | Behavior |
19
- | ----------------- | ------------------------------------------------------------ |
20
- | `[path]` | Project root, defaulting to the current directory |
21
- | `--json` | Add a machine-readable record on stdout, keeping the frame |
22
- | `--arrivals-only` | Run the gating check alone, printing nothing on a clean pass |
23
-
24
- ## Why arrival rather than the corpus
25
-
26
- `standards/standard.md` states that a criterion is added to an existing standard when that standard is next exercised, not in a sweep: a criterion written with no failure to point at is the taste-based edit the rule exists to prevent. Gating the whole corpus would fail every push until every standard already short the section was closed at once, which is the sweep that rule forbids. The check reads the whole corpus and fails only on a file present in the working tree and absent at the branch's merge base, treating a rename into the corpus the same as a standard authored fresh.
27
-
28
- ## Exit codes and refusals
29
-
30
- | Code | Meaning |
31
- | ---- | ---------------------------------------------------------------------- |
32
- | `0` | every arriving standard carries the section |
33
- | `1` | refused, with `reason` naming the cause |
34
- | `2` | a standard new to this branch states no criterion under either heading |
35
-
36
- A project authoring no standards refuses with `no-corpus`, the ordinary state of most targets, the same absence `canon claude skills audit` reads as its own `no-corpus`.
37
-
38
- ## What it does not measure
39
-
40
- Presence of the heading is the whole check. The section's content, the questions it must answer or the task it must let a reader complete, is a judgment `canon standards audit` cannot read, so a standard carrying an empty or token section still passes.
@@ -1,19 +0,0 @@
1
- ---
2
- description: Route .cspell/ dictionary additions to the right seeded file and keep each sorted
3
- paths:
4
- - '.cspell/**'
5
- ---
6
-
7
- # Spelling standards
8
-
9
- ## Dictionary routing
10
-
11
- - Rewrite a typo rather than adding it. Add only a real term.
12
- - Add a term from a tool, library, or platform the project depends on to `.cspell/tech-stack.txt`, whether it is the dependency's own name or vocabulary from its config, API, or spec.
13
- - Add jargon, an acronym, a handle, or another project-specific term to `.cspell/project-terms.txt`.
14
- - Leave a dictionary declared `addWords: false` alone. It mirrors a closed set maintained elsewhere in the project rather than approved vocabulary, so route a word there through whatever maintains that set instead of editing the dictionary file.
15
- - Add a third dictionary only when cspell still flags a term neither file covers. Declare its path in `cspell.json` with `addWords: true` before adding words to it.
16
-
17
- ## File shape
18
-
19
- - Keep every dictionary file sorted alphabetically.
@@ -1,2 +0,0 @@
1
- extends = "node"
2
- rules = ["210-astro", "300-testing-ts", "305-e2e-reliability", "306-test-scope", "350-security-web", "400-ui", "410-a11y", "420-forms", "430-ux-completeness", "440-surface-capture", "450-link-behavior", "460-design-taste", "465-interface-casing","470-motion"]
@@ -1,2 +0,0 @@
1
- extends = "react"
2
- rules = ["230-nextjs"]
@@ -1,2 +0,0 @@
1
- extends = "node"
2
- rules = ["360-security-server", "370-database"]
@@ -1,2 +0,0 @@
1
- extends = "python"
2
- rules = ["220-fastapi"]
@@ -1,2 +0,0 @@
1
- extends = "node"
2
- rules = ["200-react", "250-tailwind", "300-testing-ts", "305-e2e-reliability", "306-test-scope", "310-zod", "350-security-web", "400-ui", "410-a11y", "420-forms", "430-ux-completeness", "440-surface-capture", "450-link-behavior", "460-design-taste", "465-interface-casing","470-motion"]