@erclx/canon 5.44.0 → 5.46.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 (253) hide show
  1. package/README.md +1 -1
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/REQUIREMENT.md +15 -31
  4. package/claude/skills/auto-ship/REQUIREMENT.md +14 -25
  5. package/claude/skills/backlog-triage/REQUIREMENT.md +12 -23
  6. package/claude/skills/build-in-slices/REQUIREMENT.md +15 -25
  7. package/claude/skills/canon-cli/REQUIREMENT.md +15 -27
  8. package/claude/skills/canon-cli/SKILL.md +0 -1
  9. package/claude/skills/canon-feedback/REQUIREMENT.md +10 -18
  10. package/claude/skills/canon-feedback-triage/REQUIREMENT.md +10 -15
  11. package/claude/skills/canvas-board/REQUIREMENT.md +14 -28
  12. package/claude/skills/ci-workflow/REQUIREMENT.md +12 -21
  13. package/claude/skills/code-craft/REQUIREMENT.md +14 -28
  14. package/claude/skills/codebase-layout/REQUIREMENT.md +14 -31
  15. package/claude/skills/context-fold/REQUIREMENT.md +15 -39
  16. package/claude/skills/context-fold/references/context.md +3 -3
  17. package/claude/skills/create-rule/REQUIREMENT.md +10 -24
  18. package/claude/skills/create-rule/SKILL.md +3 -0
  19. package/claude/skills/create-skill/REQUIREMENT.md +10 -21
  20. package/claude/skills/decision-escalate/REQUIREMENT.md +12 -24
  21. package/claude/skills/deploy-app/REQUIREMENT.md +12 -22
  22. package/claude/skills/deprecation-migration/REQUIREMENT.md +14 -42
  23. package/claude/skills/design-identity/REQUIREMENT.md +14 -20
  24. package/claude/skills/design-taste/REQUIREMENT.md +16 -57
  25. package/claude/skills/docs-sync/REQUIREMENT.md +14 -23
  26. package/claude/skills/document-health/REQUIREMENT.md +15 -29
  27. package/claude/skills/document-health/SKILL.md +2 -2
  28. package/claude/skills/document-repair/REQUIREMENT.md +32 -0
  29. package/claude/skills/document-repair/SKILL.md +76 -0
  30. package/claude/skills/document-repair/references/batches.md +44 -0
  31. package/claude/skills/draft-and-pick/REQUIREMENT.md +4 -3
  32. package/claude/skills/draft-doc/REQUIREMENT.md +4 -3
  33. package/claude/skills/draft-figure/REQUIREMENT.md +4 -3
  34. package/claude/skills/draft-identity/REQUIREMENT.md +4 -3
  35. package/claude/skills/draft-ready/REQUIREMENT.md +4 -3
  36. package/claude/skills/draft-screencast/REQUIREMENT.md +4 -3
  37. package/claude/skills/draft-slides/REQUIREMENT.md +4 -3
  38. package/claude/skills/draw-figure/REQUIREMENT.md +15 -39
  39. package/claude/skills/git-branch/REQUIREMENT.md +12 -26
  40. package/claude/skills/git-commit/REQUIREMENT.md +11 -15
  41. package/claude/skills/git-followup/REQUIREMENT.md +11 -22
  42. package/claude/skills/git-issue/REQUIREMENT.md +9 -15
  43. package/claude/skills/git-pr/REQUIREMENT.md +13 -32
  44. package/claude/skills/git-pr/SKILL.md +10 -2
  45. package/claude/skills/git-pr/references/closing-issues.md +47 -0
  46. package/claude/skills/git-pr/references/pr.md +4 -0
  47. package/claude/skills/git-ship/REQUIREMENT.md +13 -26
  48. package/claude/skills/git-split/REQUIREMENT.md +10 -17
  49. package/claude/skills/git-stage/REQUIREMENT.md +12 -18
  50. package/claude/skills/git-worktree/REQUIREMENT.md +15 -19
  51. package/claude/skills/make-deck/REQUIREMENT.md +13 -21
  52. package/claude/skills/markdown-craft/REQUIREMENT.md +9 -3
  53. package/claude/skills/pick-by-eye/REQUIREMENT.md +17 -67
  54. package/claude/skills/plan-feature/REQUIREMENT.md +15 -24
  55. package/claude/skills/plan-groundwork/REQUIREMENT.md +12 -28
  56. package/claude/skills/plan-groundwork/SKILL.md +5 -2
  57. package/claude/skills/plan-groundwork/references/output.md +47 -1
  58. package/claude/skills/plan-intake/REQUIREMENT.md +11 -26
  59. package/claude/skills/plan-intake/references/intake.md +2 -2
  60. package/claude/skills/plan-intake-answer/REQUIREMENT.md +14 -29
  61. package/claude/skills/plan-intake-answer/SKILL.md +7 -1
  62. package/claude/skills/plan-ready/REQUIREMENT.md +17 -36
  63. package/claude/skills/pr-evidence/REQUIREMENT.md +11 -17
  64. package/claude/skills/read-frames/REQUIREMENT.md +3 -8
  65. package/claude/skills/record-screencast/REQUIREMENT.md +14 -17
  66. package/claude/skills/review-address/REQUIREMENT.md +14 -38
  67. package/claude/skills/review-craft/REQUIREMENT.md +14 -32
  68. package/claude/skills/review-pr/REQUIREMENT.md +15 -34
  69. package/claude/skills/review-ui/REQUIREMENT.md +14 -39
  70. package/claude/skills/role-orchestrator/REQUIREMENT.md +16 -99
  71. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +5 -0
  72. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +9 -0
  73. package/claude/skills/role-planner/REQUIREMENT.md +15 -73
  74. package/claude/skills/role-reviewer/REQUIREMENT.md +15 -65
  75. package/claude/skills/role-reviewer/SKILL.md +5 -1
  76. package/claude/skills/role-worker/REQUIREMENT.md +13 -41
  77. package/claude/skills/role-worker/SKILL.md +2 -0
  78. package/claude/skills/script-screencast/REQUIREMENT.md +10 -16
  79. package/claude/skills/search-craft/REQUIREMENT.md +12 -36
  80. package/claude/skills/search-craft/SKILL.md +1 -1
  81. package/claude/skills/search-craft/references/adopted.md +2 -2
  82. package/claude/skills/session-compact/REQUIREMENT.md +13 -23
  83. package/claude/skills/session-map/REQUIREMENT.md +13 -29
  84. package/claude/skills/session-relay/REQUIREMENT.md +14 -58
  85. package/claude/skills/session-relay/SKILL.md +6 -0
  86. package/claude/skills/session-resume/REQUIREMENT.md +13 -27
  87. package/claude/skills/session-worktree/REQUIREMENT.md +14 -43
  88. package/claude/skills/systematic-debugging/REQUIREMENT.md +13 -34
  89. package/claude/skills/target-check/REQUIREMENT.md +16 -23
  90. package/claude/skills/target-setup/REQUIREMENT.md +11 -34
  91. package/claude/skills/target-setup/SKILL.md +2 -2
  92. package/claude/skills/target-setup/references/detect.md +0 -1
  93. package/claude/skills/target-setup/references/verify.md +2 -2
  94. package/claude/skills/task-board/REQUIREMENT.md +13 -22
  95. package/claude/skills/task-board/SKILL.md +7 -0
  96. package/claude/skills/task-board/references/tasks.md +1 -1
  97. package/claude/skills/task-complete/REQUIREMENT.md +11 -24
  98. package/claude/skills/task-complete/references/archive.md +1 -0
  99. package/claude/skills/teach-workspace/REQUIREMENT.md +14 -45
  100. package/claude/skills/teach-workspace/references/promotion.md +5 -5
  101. package/claude/skills/test-craft/REQUIREMENT.md +15 -46
  102. package/claude/skills/test-first/REQUIREMENT.md +14 -20
  103. package/claude/skills/ui-checklist/REQUIREMENT.md +14 -29
  104. package/claude/skills/ux-audit/REQUIREMENT.md +3 -7
  105. package/claude/skills/ux-measure/REQUIREMENT.md +14 -28
  106. package/claude/skills/video-craft/REQUIREMENT.md +14 -27
  107. package/claude/skills/write-doc/REQUIREMENT.md +14 -24
  108. package/claude/skills/write-human/REQUIREMENT.md +15 -27
  109. package/claude/skills/youtube-transcripts/REQUIREMENT.md +9 -15
  110. package/docs/agents/context-audit.md +19 -12
  111. package/docs/agents/install-and-sync.md +2 -2
  112. package/docs/agents/markdown-audit.md +15 -10
  113. package/docs/agents/output-shape.md +1 -2
  114. package/docs/target/migrations.md +18 -0
  115. package/docs/target/projects.md +3 -2
  116. package/docs/target/scenarios.md +4 -1
  117. package/docs/workflow/skill-map-on-request.md +1 -1
  118. package/docs/workflow/skill-map.md +6 -5
  119. package/governance/detect.toml +4 -0
  120. package/governance/ledger/lang/go.md +61 -0
  121. package/governance/ledger/lang/php.md +50 -0
  122. package/governance/ledger/lang/python.md +68 -0
  123. package/governance/ledger/lang/typescript.md +64 -0
  124. package/governance/ledger/lib/database.md +65 -0
  125. package/governance/ledger/lib/e2e-reliability.md +57 -0
  126. package/governance/ledger/lib/pydantic.md +61 -0
  127. package/governance/ledger/lib/security-server.md +62 -0
  128. package/governance/ledger/lib/security-web.md +51 -0
  129. package/governance/ledger/lib/tanstack-query.md +57 -0
  130. package/governance/ledger/lib/test-scope.md +43 -0
  131. package/governance/ledger/lib/testing-go.md +57 -0
  132. package/governance/ledger/lib/testing-php.md +51 -0
  133. package/governance/ledger/lib/testing-py.md +67 -0
  134. package/governance/ledger/lib/testing-ts.md +63 -0
  135. package/governance/ledger/lib/zod.md +47 -0
  136. package/governance/ledger/tooling/ci-workflow.md +33 -0
  137. package/governance/ledger/tooling/code-comments.md +48 -0
  138. package/governance/ledger/tooling/config-comments.md +27 -0
  139. package/governance/ledger/tooling/dependencies.md +38 -0
  140. package/governance/ledger/ui/a11y.md +55 -0
  141. package/governance/ledger/ui/copy.md +49 -0
  142. package/governance/ledger/ui/design-reference.md +42 -0
  143. package/governance/ledger/ui/design-taste.md +19 -0
  144. package/governance/ledger/ui/forms.md +71 -0
  145. package/governance/ledger/ui/interface-casing.md +47 -0
  146. package/governance/ledger/ui/keyboard.md +68 -0
  147. package/governance/ledger/ui/link-behavior.md +29 -0
  148. package/governance/ledger/ui/motion.md +33 -0
  149. package/governance/ledger/ui/surface-capture.md +69 -0
  150. package/governance/ledger/ui/ux-completeness.md +79 -0
  151. package/governance/renames.toml +0 -2
  152. package/governance/rules/claude/output.md +5 -1
  153. package/governance/rules/code/routing.md +5 -1
  154. package/governance/rules/lang/go.md +7 -16
  155. package/governance/rules/lang/php.md +3 -3
  156. package/governance/rules/lang/python.md +9 -18
  157. package/governance/rules/lang/typescript.md +9 -19
  158. package/governance/rules/lib/database.md +5 -5
  159. package/governance/rules/lib/e2e-reliability.md +4 -6
  160. package/governance/rules/lib/pydantic.md +14 -16
  161. package/governance/rules/lib/security-server.md +4 -4
  162. package/governance/rules/lib/security-web.md +6 -10
  163. package/governance/rules/lib/tanstack-query.md +10 -12
  164. package/governance/rules/lib/test-scope.md +4 -4
  165. package/governance/rules/lib/testing-go.md +1 -2
  166. package/governance/rules/lib/testing-php.md +4 -5
  167. package/governance/rules/lib/testing-py.md +11 -19
  168. package/governance/rules/lib/testing-ts.md +7 -16
  169. package/governance/rules/lib/zod.md +6 -8
  170. package/governance/rules/tooling/code-comments.md +2 -4
  171. package/governance/rules/tooling/config-comments.md +1 -4
  172. package/governance/rules/tooling/dependencies.md +1 -1
  173. package/governance/rules/ui/a11y.md +16 -45
  174. package/governance/rules/ui/copy.md +6 -9
  175. package/governance/rules/ui/forms.md +13 -19
  176. package/governance/rules/ui/interface-casing.md +5 -7
  177. package/governance/rules/ui/keyboard.md +38 -0
  178. package/governance/rules/ui/link-behavior.md +3 -3
  179. package/governance/rules/ui/motion.md +3 -3
  180. package/governance/rules/ui/surface-capture.md +15 -27
  181. package/governance/rules/ui/ux-completeness.md +16 -39
  182. package/governance/stacks/base.toml +0 -1
  183. package/package.json +1 -1
  184. package/scripts/core/install-check.sh +1 -1
  185. package/src/claude/cases/workflow.ts +12 -0
  186. package/src/cli/help.ts +0 -1
  187. package/src/cli.ts +0 -2
  188. package/src/commands/context.ts +57 -3
  189. package/src/commands/gov.ts +4 -0
  190. package/src/commands/init.ts +2 -2
  191. package/src/commands/labels.ts +83 -3
  192. package/src/commands/markdown.ts +188 -21
  193. package/src/commands/sessions.ts +36 -3
  194. package/src/commands/tasks.ts +180 -0
  195. package/src/commands/tooling.ts +18 -5
  196. package/src/commands/worktrees.ts +2 -0
  197. package/src/context/coverage.ts +210 -0
  198. package/src/context/gate.ts +12 -3
  199. package/src/design/components.ts +1 -1
  200. package/src/gate/stages.ts +1 -1
  201. package/src/gov/audit.ts +22 -2
  202. package/src/init/plan.ts +1 -8
  203. package/src/init/steps.ts +44 -7
  204. package/src/intake/items.ts +14 -0
  205. package/src/inventory/subjects.ts +1 -1
  206. package/src/labels/closing.ts +46 -0
  207. package/src/markdown/gated.ts +250 -0
  208. package/src/markdown/scope.ts +119 -2
  209. package/src/migrate/rename.ts +6 -0
  210. package/src/migrate/scratch-evidence.ts +12 -4
  211. package/src/records/backup.ts +3 -3
  212. package/src/records/validate.ts +3 -3
  213. package/src/roots/record.ts +17 -8
  214. package/src/sessions/claim.ts +7 -0
  215. package/src/sessions/live.ts +53 -6
  216. package/src/sessions/registry.ts +11 -0
  217. package/src/sessions/resolve.ts +32 -5
  218. package/src/tasks/closes.ts +136 -0
  219. package/src/tasks/record.ts +1 -1
  220. package/src/tooling/inject.ts +26 -7
  221. package/src/tooling/package.ts +32 -0
  222. package/src/tooling/read.ts +46 -3
  223. package/src/tooling/scan.ts +8 -2
  224. package/src/worktrees/reclaim.ts +16 -1
  225. package/tooling/astro/reference.md +64 -93
  226. package/tooling/base/configs/.github/workflows/phase-label-gate.yml +1 -1
  227. package/tooling/base/configs/scripts/verify.ts +2 -2
  228. package/tooling/base/reference.md +1 -9
  229. package/tooling/base/seeds/canon/context/{ci.md → development/ci.md} +1 -1
  230. package/tooling/base/seeds/canon/context/development/index.md +11 -0
  231. package/tooling/base/seeds/canon/context/{development.md → development/setup.md} +8 -12
  232. package/tooling/base/seeds/canon/context/index.md +1 -2
  233. package/tooling/claude/manifest.toml +1 -5
  234. package/tooling/claude/reference.md +36 -59
  235. package/tooling/claude/seeds/.claude/hooks/board-validate.sh +1 -1
  236. package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +2 -7
  237. package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +2 -7
  238. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +3 -10
  239. package/tooling/claude/seeds/.claude/hooks/search-reminder.sh +2 -7
  240. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +2 -4
  241. package/tooling/claude/seeds/CLAUDE.md +2 -2
  242. package/tooling/nextjs/reference.md +0 -6
  243. package/tooling/php/reference.md +0 -4
  244. package/tooling/web/configs/.github/workflows/verify.yml +28 -0
  245. package/tooling/web/configs/.oxlintrc.json +7 -0
  246. package/tooling/web/configs/e2e/capture-types.ts +35 -0
  247. package/tooling/web/configs/e2e/screenshot.ts +23 -72
  248. package/tooling/web/manifest.toml +1 -1
  249. package/tooling/web/reference.md +77 -124
  250. package/tooling/web/seeds/e2e/capture/cases.ts +47 -0
  251. package/governance/rules/canon/task-scope.md +0 -12
  252. package/src/commands/wiki.ts +0 -100
  253. package/src/wiki/init.ts +0 -85
@@ -11,9 +11,9 @@ paths:
11
11
 
12
12
  ## Default target
13
13
 
14
- - Open a link in the same tab. Reserve `target="_blank"` for a destination that would discard in-progress work if it replaced the current page, such as a document a user is midway through elsewhere.
15
- - Do not add `target="_blank"` as a default for every external link.
14
+ - Open a link in the same tab. Reserve `target="_blank"` for a page the person must consult while finishing a task in the current one.
15
+ - Never add `target="_blank"` as a default for every external link.
16
16
 
17
17
  ## mailto: links
18
18
 
19
- - Open a `mailto:` link in the same tab. It hands off to the mail client rather than replacing page content, so a new tab leaves an empty tab behind.
19
+ - Open a `mailto:` link in the same tab. It hands off to the mail client rather than replacing the page, so a new tab leaves an empty tab behind.
@@ -12,12 +12,12 @@ paths:
12
12
 
13
13
  ## Moving content
14
14
 
15
- - Give any content that moves, blinks, or scrolls automatically for more than five seconds a control to pause, stop, or hide it (WCAG Pause, Stop, Hide, 2.2.2).
15
+ - Give any content that moves, blinks, or scrolls automatically for more than five seconds a control to pause, stop, or hide it (WCAG 2.2.2).
16
16
  - Halt an auto-advancing carousel or ticker while it holds keyboard focus or pointer hover.
17
- - Do not flash content more than three times in any one second.
17
+ - Never flash content more than three times in any one second (WCAG 2.3.1).
18
18
 
19
19
  ## Reduced motion
20
20
 
21
21
  - Honor `prefers-reduced-motion: reduce` for every animation and transition that conveys no state. Remove it or replace it with an instant change.
22
22
  - Keep essential state changes visible under reduced motion, using a fade or a static indicator instead of movement.
23
- - Do not start video or animated backgrounds on their own when the preference is set.
23
+ - Start no video or animated background on its own when the preference is set.
@@ -12,39 +12,27 @@ paths:
12
12
 
13
13
  ## What a surface is
14
14
 
15
- - A surface is anything the project renders for a person to look at. A framework route is one. A page the project generates is another, whoever generates it.
16
- - Judge a generated page by the same rule as a route. Reading its markup reports nothing about how it composes.
17
- - A component the production build strips out is not a surface, such as a dev-only scenario switcher gated behind a build flag. The build removes it before anything renders, so no comparison exists to capture.
15
+ - Treat anything the project renders for a person as a surface, a generated page as much as a route.
16
+ - Skip a component the production build strips out, such as a dev-only switcher.
18
17
 
19
18
  ## When to capture
20
19
 
21
- - Run the project's capture command after changing what a surface renders. Report it rather than proceeding silently when the project has none.
22
- - Capture against a running preview server. Do not capture against a dev server.
23
- - Capture every theme the surface ships. Do not capture the default theme alone.
24
- - Capture again after fixing a defect a capture found. A repair inside a shared stylesheet can cancel a rule written earlier in the same file.
20
+ - Run the project's capture command against a preview server, never a dev server, after changing a surface. Report it when the project has none.
21
+ - Capture every theme the surface ships, and again after fixing a defect a capture found.
25
22
 
26
23
  ## What a capture covers
27
24
 
28
- - Capture the full page. Do not capture a component in isolation.
29
- - Declare the widths a layout case captures. Always include 320, the reflow floor from the reflow criterion in `a11y`, then add each breakpoint bucket the project writes rules for, read from the `## Layout` section of `DESIGN.md`.
30
- - Fall back to the 320 floor alone when the project has no `DESIGN.md` or its record carries no `## Layout` section. Do not impose another project's breakpoints.
31
- - Add a layout case at each bound when a surface renders persistent chrome a person can resize, such as a draggable sidebar or a split pane, one with the chrome at its narrowest and one at its widest. Take both at the narrowest declared width where the chrome stays persistent rather than collapsing, since a frame where it has turned into an overlay shows none of the width it takes.
32
- - Read those bounds from the code that clamps the chrome, since `## Layout` has no slot for them. Judge each frame by the content width left beside the chrome rather than by the window width, against the 320 floor `a11y` holds for that region.
33
- - Apply the declaration to a case that covers layout only. A case driving a menu open or an answer chosen tests a state, so it takes one width rather than the full set.
34
- - Read a capture at 320 as proof that a frame was taken there, not that the page reflows correctly. The criterion is stated in `a11y` and nothing here enforces it.
35
- - Write the capture into a folder named `evidence`. The image is collected for the pull request comment when any segment of its path is literally `evidence` and its filename carries an image extension, so the folder name is fixed and its position in the tree is the project's own.
36
- - Several `evidence/` folders in one project is the ordinary shape rather than a problem to consolidate. One per surface is how a project with more than one rendered surface ends up.
37
- - Drive a state a screenshot cannot reach. An initial render reports nothing about a menu that opens, an answer that is chosen, or a rail that tracks scrolling.
38
- - Add a case to the capture record when adding a surface.
39
- - Remove a surface's case in the change that removes the surface.
25
+ - Capture the full page, never a component in isolation.
26
+ - Capture a layout case at 320, the reflow floor in `a11y`, plus each breakpoint in the `## Layout` section of `DESIGN.md`, or at 320 alone when there is none.
27
+ - Capture resizable chrome at its narrowest and widest clamp, at the narrowest width keeping it persistent. Judge the content width beside it.
28
+ - Capture a state case at one width rather than the full set.
29
+ - Drive a state an initial render cannot reach, such as an open menu or a rail tracking scroll.
30
+ - Add a surface's case with the surface, and remove it with the surface.
40
31
 
41
32
  ## Sharing a capture
42
33
 
43
- - Do not commit the sweep. It stays ignored.
44
- - Commit a flagged case's evidence output so the pull request carries the comparison, rather than attaching it by hand.
45
- - A project whose own established convention already commits that same comparison may route a flagged case's evidence there instead of a separate evidence folder. Only an established convention already committing the same comparison qualifies, not a preference for skipping a second folder.
46
- - Commit an evidence case for the first time only after running the capture twice with no code change between the runs and confirming the two outputs are byte-identical.
47
- - Mask the element carrying a value that moves between runs, such as an elapsed-time counter, a clock, or a relative date, in the state's capture case before the double capture, so the check still runs on everything else in the frame. Mask the smallest element holding the value, never the state.
48
- - Do not commit the state as evidence when the masked outputs still differ, which happens when the value's width reflows its neighbors. Name the variance in the pull request instead, and never re-run toward a match.
49
- - Recommitting an unbounded sweep on every run reaches a gigabyte of repository history inside a hundred merges. A small, committed evidence set is what a reviewer needs and what lets GitHub draw its own before-and-after comparison on the pull request.
50
- - `git-pr` and `git-followup` maintain a comparison comment on the pull request automatically once a case is committed, so a reviewer never has to open Files Changed to see it.
34
+ - Write a capture into a folder named `evidence`, which the pull request comment collects from.
35
+ - Commit a flagged case's evidence output and never the sweep, unless the project already commits the same comparison elsewhere.
36
+ - Commit a new evidence case only after two captures with no code change between them match byte for byte.
37
+ - Mask the smallest element holding a value that moves between runs, such as a clock, before the double capture.
38
+ - Name the variance in the pull request when masked outputs still differ, never re-running toward a match.
@@ -10,56 +10,33 @@ paths:
10
10
 
11
11
  # UX completeness standards
12
12
 
13
- ## Mutation feedback
13
+ ## Feedback
14
14
 
15
- - Every user-initiated mutation (save, delete, sync, import, export) must show inline feedback on success.
16
- - Feedback must be transient: auto-dismiss after 2-3 seconds without requiring user action.
17
- - Show inline error feedback when a mutation fails. Do not fail silently.
15
+ - Confirm every user-initiated mutation, such as a save, delete, or import, inline on success, and show an inline error when it fails.
16
+ - Auto-dismiss a success message only when its outcome stays visible elsewhere. Keep a message carrying the only copy of its information until dismissed (WCAG 2.2.1).
17
+ - Show no optimistic status, such as a green dot or "connected", while unsaved edits are pending.
18
18
 
19
19
  ## Empty states
20
20
 
21
- - Every list, table, or collection view must handle the empty state explicitly.
22
- - Empty states must include a next action: a button, link, or instruction that tells the user what to do.
23
- - Distinguish between "nothing exists yet" (onboarding) and "nothing matches your filter" (recovery). Use different copy for each.
24
- - Filter-driven empty states must include a way to clear the filter (link or button).
21
+ - Give every list, table, or collection an explicit empty state carrying a next action, such as a button or link.
22
+ - Write different copy for "nothing exists yet" and "nothing matches your filter", and give the filtered state a way to clear the filter.
25
23
 
26
24
  ## Loading states
27
25
 
28
- - Every async operation visible to the user must show a loading indicator (spinner, skeleton, or disabled state).
29
- - Do not show stale content while new content is loading. Either show a loading state or keep the previous content with a progress indicator.
26
+ - Show a progress indicator for any operation a person waits on for longer than about one second.
27
+ - Mark previous content as updating while new content loads, rather than showing it unmarked as current.
30
28
 
31
29
  ## Destructive actions
32
30
 
33
- - Destructive actions (delete, disconnect, discard) must require confirmation before executing.
34
- - Confirmation UI must be dismissible via Escape in addition to a cancel button.
31
+ - Confirm a destructive action, such as a delete, disconnect, or discard, before it runs, unless it can be undone afterward.
35
32
 
36
- ## Inline confirmations
33
+ ## Element states
37
34
 
38
- - All inline confirmation rows (delete, discard, cancel) must be dismissible via Escape.
39
- - Confirmation labels and destructive buttons must have sufficient contrast in both light and dark modes.
35
+ - Give every interactive element distinct hover, active, focus, and disabled styles.
36
+ - Never flash an intermediate state, such as a light frame before a dark theme or a layout jump before data arrives. Reserve the space or settle it first.
40
37
 
41
- ## Status indicators
38
+ ## Overflow
42
39
 
43
- - Status indicators (dots, badges, connection status) must reset to a neutral state when their source input changes.
44
- - Do not show optimistic status (green dot, "connected") while unsaved edits are pending.
45
-
46
- ## State transitions
47
-
48
- - Do not flash intermediate states during transitions (e.g., white flash on theme load, layout jump before data arrives).
49
- - Use CSS transitions or deferred rendering to prevent visible flicker between states.
50
-
51
- ## Interactive element states
52
-
53
- - All interactive elements must visually respond to hover, active, focus, and disabled states.
54
- - Disabled elements must use reduced opacity or muted colors and `cursor: not-allowed`.
55
- - Do not rely on a single state (e.g., focus only) to communicate interactivity.
56
-
57
- ## Overflow and truncation
58
-
59
- - Long text in constrained containers must truncate with ellipsis, not overflow or wrap unexpectedly.
60
- - Truncated text must be accessible via `title` attribute or expandable interaction.
61
-
62
- ## Scroll and clipping
63
-
64
- - Focus rings and interactive borders must not be clipped by parent `overflow: hidden` or `overflow: auto` containers.
65
- - Add padding or adjust overflow to prevent clipping on edge items in scrollable lists.
40
+ - Truncate long text in a constrained container with an ellipsis rather than letting it overflow.
41
+ - Expose truncated text through an expand control or full text on focus, never through a `title` tooltip alone.
42
+ - Keep focus rings and interactive borders clear of a parent's `overflow` clipping, padding the edge items of a scrollable list.
@@ -4,7 +4,6 @@ extends = ""
4
4
  # `canon gov detect`. Every other rule is opt-in.
5
5
  rules = [
6
6
  "output",
7
- "task-scope",
8
7
  "scratch",
9
8
  "worktrees",
10
9
  "git",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "5.44.0",
4
+ "version": "5.46.0",
5
5
  "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -100,7 +100,7 @@ log_step "Assert scaffold"
100
100
  # Authoring references name no path because they ship inside the plugin's
101
101
  # skills rather than installing into a target. The two
102
102
  # rule paths stand for the `claude` and `canon` groups `base` takes whole.
103
- for path in "CLAUDE.md" ".claude/wiki/index.md" ".claude" "canon/context/index.md" \
103
+ for path in "CLAUDE.md" ".claude" "canon/context/index.md" \
104
104
  ".oxfmtrc.json" ".lintstagedrc" ".husky/pre-commit" ".github/workflows/verify.yml" "scripts/verify.ts" \
105
105
  ".claude/rules/canon/claude/output.md" ".claude/rules/canon/canon/scratch.md"; do
106
106
  if [ ! -e "$TARGET_DIR/$path" ]; then
@@ -146,6 +146,18 @@ export const WORKFLOW_CASES: readonly SkillCase[] = [
146
146
  'Are our requirements and architecture docs still true, and when were they last reviewed?',
147
147
  expect: 'document-health',
148
148
  },
149
+ // A repair rather than a report, and folder by folder until each gates,
150
+ // which is what separates it from `document-health` above.
151
+ {
152
+ prompt:
153
+ 'Our docs folder fails the paragraph and bullet limits everywhere, so rewrite it until it passes and lock it in.',
154
+ expect: 'document-repair',
155
+ },
156
+ {
157
+ prompt:
158
+ 'Bring the context entries under the density limits one folder at a time and gate each once it is clean.',
159
+ expect: 'document-repair',
160
+ },
149
161
  {
150
162
  prompt: 'Open a new entry on the task board for this piece of work.',
151
163
  expect: 'task-board',
package/src/cli/help.ts CHANGED
@@ -29,7 +29,6 @@ export const COMMAND_GROUPS: { title: string; rows: CommandRow[] }[] = [
29
29
  ['gov [cmd]', 'Governance rules'],
30
30
  ['tooling [cmd]', 'Tooling stacks'],
31
31
  ['claude [cmd]', 'Claude workflow'],
32
- ['wiki [cmd]', 'Wiki pages'],
33
32
  ['indexes [cmd]', 'Regenerate index.md files'],
34
33
  ['docs [cmd|topic]', 'Emit toolkit reference docs'],
35
34
  ],
package/src/cli.ts CHANGED
@@ -7,7 +7,6 @@ import { register as sync } from '@/commands/sync'
7
7
  import { register as gov } from '@/commands/gov'
8
8
  import { register as tooling } from '@/commands/tooling'
9
9
  import { register as claude } from '@/commands/claude'
10
- import { register as wiki } from '@/commands/wiki'
11
10
  import { register as indexes } from '@/commands/indexes'
12
11
  import { register as docs } from '@/commands/docs'
13
12
  import { register as design } from '@/commands/design'
@@ -68,7 +67,6 @@ sync(program)
68
67
  gov(program)
69
68
  tooling(program)
70
69
  claude(program)
71
- wiki(program)
72
70
  indexes(program)
73
71
  docs(program)
74
72
  design(program)
@@ -56,6 +56,11 @@ import {
56
56
  presentNames,
57
57
  resolveFolders,
58
58
  } from '@/context/folders'
59
+ import {
60
+ type CoverageReport,
61
+ measureCoverage,
62
+ readRoots,
63
+ } from '@/context/coverage'
59
64
  import { isGating } from '@/context/gate'
60
65
  import { isShapeGated, SHAPE_GATED_FOLDERS } from '@/context/shape-gated'
61
66
  import { currentWorktreeRoot } from '@/git/worktree'
@@ -101,7 +106,7 @@ export function register(program: Command): void {
101
106
  context
102
107
  .command('audit')
103
108
  .description(
104
- 'Report required sections, entry length, citations, reference form, catalog tables, provenance, index drift, and the requirements record against its own word cap',
109
+ 'Report required sections, entry length, citations, reference form, catalog tables, provenance, index drift, code folders no entry names, and the requirements record against its own word cap',
105
110
  )
106
111
  .argument('[path]', 'Project root, defaulting to the current directory')
107
112
  .helpOption('-h, --help', 'Show this help message')
@@ -113,7 +118,7 @@ export function register(program: Command): void {
113
118
  .option('--citations-only', 'Run the gating citation check alone')
114
119
  .option(
115
120
  '--gate',
116
- 'Also fail on a missing required section, an H2 outside the set, a shape break in a gated folder, or index drift, the findings that are facts',
121
+ 'Also fail on a missing required section, an H2 outside the set, a shape break in a gated folder, index drift, or a code folder no entry names, the findings that are facts',
117
122
  )
118
123
  .addHelpText(
119
124
  'after',
@@ -129,7 +134,9 @@ export function register(program: Command): void {
129
134
  '--citations-only, which never measures it. A record stating no',
130
135
  'cap is reported and never gated. --gate widens the gate to the',
131
136
  'findings that are facts rather than judgments: a missing',
132
- 'required section, an H2 outside the closed set, and index drift.',
137
+ 'required section, an H2 outside the closed set, index drift, and',
138
+ 'a code folder no Layout names or two entries claim. The roots come',
139
+ 'from canon/config/context.toml and default to src when present.',
133
140
  'A context entry requires Overview and Layout and allows Gotchas.',
134
141
  'A shape break, such as a Decisions section or a sixth gotcha, is',
135
142
  'reported everywhere and gated only under a shape-gated folder.',
@@ -667,6 +674,9 @@ async function runAudit(
667
674
  isShapeGated(relative(top, join(root, rel))),
668
675
  )
669
676
  const length = gateOnly ? undefined : lengthFindings(entries)
677
+ const coverage: CoverageReport | undefined = gateOnly
678
+ ? undefined
679
+ : measureCoverage(root, readRoots(root), folders)
670
680
  // Absent under `--citations-only` and null when the project carries no
671
681
  // record. A run that never looked and a project with nothing to look at are
672
682
  // different answers, and one value for both reports the second as the first.
@@ -685,6 +695,7 @@ async function runAudit(
685
695
  reportTables(entries)
686
696
  reportProvenance(entries, folders)
687
697
  reportDrift(drift)
698
+ reportCoverage(coverage ?? { kind: 'no-root' })
688
699
  reportRequirements(requirements, root)
689
700
  outro()
690
701
  }
@@ -721,6 +732,8 @@ async function runAudit(
721
732
  extraSections: extras,
722
733
  shapeFindings: shape,
723
734
  indexDrift: drift,
735
+ // Absent under `--citations-only`, which never measures it.
736
+ coverage,
724
737
  // Null says the run opened the project and found no record, which a
725
738
  // target that never wrote one is entitled to. Absent says the run
726
739
  // never looked, which is `--citations-only`.
@@ -753,6 +766,7 @@ async function runAudit(
753
766
  extraSections: extras,
754
767
  shape,
755
768
  drift,
769
+ coverage: coverage ?? { kind: 'no-root' },
756
770
  widened,
757
771
  })
758
772
 
@@ -1210,6 +1224,46 @@ function reportProvenance(
1210
1224
  )
1211
1225
  }
1212
1226
 
1227
+ function reportCoverage(report: CoverageReport): void {
1228
+ logStep('Coverage')
1229
+
1230
+ if (report.kind === 'no-root') {
1231
+ logInfo(
1232
+ 'No code root found, since no canon/config/context.toml declares one and there is no src/, so nothing was measured.',
1233
+ )
1234
+ return
1235
+ }
1236
+
1237
+ if (report.kind === 'invalid') {
1238
+ logWarn(report.reason)
1239
+ return
1240
+ }
1241
+
1242
+ if (report.kind === 'opted-out') {
1243
+ logInfo(
1244
+ 'canon/config/context.toml declares roots = [], so coverage was not measured.',
1245
+ )
1246
+ return
1247
+ }
1248
+
1249
+ const { unnamed, doubled, missingRoots } = report.findings
1250
+ const lines = [
1251
+ ...missingRoots.map((root) => `${root} matches no folder`),
1252
+ ...unnamed.map((folder) => `${folder} named in no Layout`),
1253
+ ...doubled.map(
1254
+ ({ folder, rels }) => `${folder} Layout H3 in ${rels.join(', ')}`,
1255
+ ),
1256
+ ]
1257
+
1258
+ if (lines.length === 0) {
1259
+ logInfo(`Every folder under ${report.roots.join(', ')} is named once.`)
1260
+ return
1261
+ }
1262
+
1263
+ logWarn(plural(lines.length, 'coverage finding'))
1264
+ pipeOutput(lines.join('\n'))
1265
+ }
1266
+
1213
1267
  /** Reports the requirements record against the word cap it states, if any. */
1214
1268
  function reportRequirements(
1215
1269
  report: RequirementsReport | undefined,
@@ -37,6 +37,7 @@ import {
37
37
  hasFinding,
38
38
  PRACTICE_FOLDERS,
39
39
  readRuleAudit,
40
+ RULE_GATE_EXEMPT,
40
41
  RULE_GATED_FOLDERS,
41
42
  RULE_LIMITS,
42
43
  type RuleAuditReport,
@@ -631,6 +632,7 @@ export function register(program: Command): void {
631
632
  ' inside it.',
632
633
  '',
633
634
  `Gated folders: ${RULE_GATED_FOLDERS.length === 0 ? 'none yet' : RULE_GATED_FOLDERS.join(', ')}`,
635
+ `Exempt from the gate: ${RULE_GATE_EXEMPT.length === 0 ? 'none' : RULE_GATE_EXEMPT.join(', ')}`,
634
636
  '',
635
637
  'Exit codes:',
636
638
  ' 0 no finding, or under --gate no finding in a gated folder',
@@ -691,6 +693,7 @@ async function runAudit(
691
693
  root,
692
694
  limits: RULE_LIMITS,
693
695
  gated: RULE_GATED_FOLDERS,
696
+ exempt: RULE_GATE_EXEMPT,
694
697
  ...report,
695
698
  })}\n`,
696
699
  )
@@ -699,6 +702,7 @@ async function runAudit(
699
702
  return auditExit(report, {
700
703
  gate: opts.gate ?? false,
701
704
  gated: RULE_GATED_FOLDERS,
705
+ exempt: RULE_GATE_EXEMPT,
702
706
  })
703
707
  }
704
708
 
@@ -39,8 +39,8 @@ export function register(program: Command): void {
39
39
  '',
40
40
  'Runs `canon tooling sync base --write` first, which replaces every',
41
41
  'base golden config the target holds, then `canon claude init`,',
42
- '`canon gov install`, and `canon wiki init`. Refuses a target that is',
43
- 'not a directory or is the toolkit root.',
42
+ 'and `canon gov install`. Refuses a target that is not a directory or is',
43
+ 'the toolkit root.',
44
44
  '',
45
45
  'Exit codes:',
46
46
  ' 0 every domain installed, or the prompt was cancelled',
@@ -5,6 +5,7 @@ import { execa } from 'execa'
5
5
  import { gitEnv } from '@/git/env'
6
6
  import { diffLabels } from '@/labels/apply'
7
7
  import { type LabelAuditRefusal, auditLabels } from '@/labels/audit'
8
+ import { checkClosing } from '@/labels/closing'
8
9
  import { resolveScanInput } from '@/labels/event'
9
10
  import { checkTitleFormat, type TitleFormatIssue } from '@/labels/format'
10
11
  import { MAP_REL } from '@/labels/map'
@@ -34,6 +35,7 @@ interface ScanOptions {
34
35
  readonly body?: string
35
36
  readonly bodyFile?: string
36
37
  readonly head?: string
38
+ readonly closes?: string
37
39
  readonly json?: boolean
38
40
  }
39
41
 
@@ -196,6 +198,10 @@ export function register(program: Command): void {
196
198
  'Body to scan, read from a file, overriding the event payload',
197
199
  )
198
200
  .option('--head <ref>', 'Head branch, overriding the event payload')
201
+ .option(
202
+ '--closes <numbers>',
203
+ 'Issues the body must close, a comma list or `none`, graded when given',
204
+ )
199
205
  .option('--json', 'Add a machine-readable record on stdout')
200
206
  .addHelpText(
201
207
  'after',
@@ -239,12 +245,20 @@ export function register(program: Command): void {
239
245
  'character, an em dash or a semicolon, in the title or body of a pull',
240
246
  'request or a review, outside a code span or a link.',
241
247
  '',
248
+ 'With --closes, it also grades the closing lines. Each issue named needs',
249
+ 'its own `Closes #NNN` keyword in the body, and a closing keyword for an',
250
+ 'issue the list leaves out fails. `none` expects no closing line.',
251
+ 'Without the flag the check does not run, so the pull request job and a',
252
+ 'followup keep their behavior. A keyword in a code span or a fence is',
253
+ 'not read, as GitHub does not read it.',
254
+ '',
242
255
  'Exit codes:',
243
- ' 0 none of the six found',
256
+ ' 0 none of the eight found',
244
257
  ' 1 refused, with the reason on stderr or in the JSON record',
245
258
  ' 2 the title or body carries a phase label, a board identifier, a',
246
259
  ' session link, a banned character, a heading outside the closed',
247
- ' list or a required one missing, or a title',
260
+ ' list or a required one missing, a missing or stray closing line',
261
+ ' under --closes, or a title',
248
262
  " breaking claude/skills/git-pr/references/pr.md's format, casing, or length",
249
263
  '',
250
264
  'Examples:',
@@ -536,11 +550,42 @@ async function runApply(number: string, opts: ApplyOptions): Promise<number> {
536
550
  return owed.length === 0 ? 0 : 2
537
551
  }
538
552
 
553
+ /**
554
+ * Reads `--closes` as the issues the body must close. Undefined when the flag
555
+ * is absent, which skips the check, and a message when the value is neither
556
+ * `none` nor a comma list of numbers, since a guessed list would grade the
557
+ * body against the wrong issues.
558
+ */
559
+ function parseCloses(value: string | undefined): number[] | string | undefined {
560
+ if (value === undefined) return undefined
561
+ if (value === 'none') return []
562
+ if (!/^\d+(,\d+)*$/.test(value)) {
563
+ return `--closes takes a comma list of issue numbers or none, not ${value}`
564
+ }
565
+
566
+ return [...new Set(value.split(',').map(Number))]
567
+ }
568
+
539
569
  async function runScan(opts: ScanOptions): Promise<number> {
540
570
  const emitJson = opts.json ?? false
541
571
 
542
572
  intro('canon labels scan')
543
573
 
574
+ const expected = parseCloses(opts.closes)
575
+
576
+ if (typeof expected === 'string') {
577
+ logStep('Refused')
578
+ logWarn(expected)
579
+ outro()
580
+
581
+ if (emitJson) {
582
+ process.stdout.write(
583
+ `${JSON.stringify({ reason: 'bad-input', message: expected })}\n`,
584
+ )
585
+ }
586
+ return 1
587
+ }
588
+
544
589
  const resolved = resolveScanInput(opts)
545
590
 
546
591
  if (resolved.kind === 'refused') {
@@ -693,6 +738,37 @@ async function runScan(opts: ScanOptions): Promise<number> {
693
738
  for (const issue of titleFormatIssues) logWarn(TITLE_FORMAT_MESSAGES[issue])
694
739
  }
695
740
 
741
+ const closing =
742
+ expected === undefined
743
+ ? { missing: [], stray: [] }
744
+ : checkClosing(resolved.body, expected)
745
+
746
+ logStep(
747
+ expected === undefined
748
+ ? 'Closing lines not checked'
749
+ : closing.missing.length === 0 && closing.stray.length === 0
750
+ ? 'Clean'
751
+ : 'Closing line issue found',
752
+ )
753
+ if (expected === undefined) {
754
+ logInfo('no --closes list, so the closing lines are not graded')
755
+ } else if (closing.missing.length === 0 && closing.stray.length === 0) {
756
+ logInfo('the closing lines match the issues the task closes')
757
+ } else {
758
+ if (closing.missing.length > 0) {
759
+ logWarn(
760
+ `${plural(closing.missing.length, 'issue')} with no closing line. Add one \`Closes #NNN\` per issue.`,
761
+ )
762
+ for (const number of closing.missing) logWarn(`#${number}`)
763
+ }
764
+ if (closing.stray.length > 0) {
765
+ logWarn(
766
+ `${plural(closing.stray.length, 'closing line')} for an issue this pull request does not close. Delete each.`,
767
+ )
768
+ for (const number of closing.stray) logWarn(`#${number}`)
769
+ }
770
+ }
771
+
696
772
  outro()
697
773
 
698
774
  if (emitJson) {
@@ -706,6 +782,8 @@ async function runScan(opts: ScanOptions): Promise<number> {
706
782
  titleFormatIssues,
707
783
  sections: { checked: sectionsChecked, ...sections },
708
784
  bannedCharacters,
785
+ missingClosures: closing.missing,
786
+ strayClosures: closing.stray,
709
787
  })}\n`,
710
788
  )
711
789
  }
@@ -716,7 +794,9 @@ async function runScan(opts: ScanOptions): Promise<number> {
716
794
  titleFormatIssues.length === 0 &&
717
795
  sections.outside.length === 0 &&
718
796
  sections.missing.length === 0 &&
719
- banFindings.length === 0
797
+ banFindings.length === 0 &&
798
+ closing.missing.length === 0 &&
799
+ closing.stray.length === 0
720
800
  ? 0
721
801
  : 2
722
802
  }