@erclx/canon 5.37.0 → 5.39.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 (197) hide show
  1. package/README.md +1 -1
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/SKILL.md +12 -9
  4. package/claude/skills/auto-ship/SKILL.md +43 -23
  5. package/claude/skills/backlog-triage/SKILL.md +62 -53
  6. package/claude/skills/backlog-triage/references/apply.md +69 -0
  7. package/claude/skills/build-in-slices/SKILL.md +8 -4
  8. package/claude/skills/canon-cli/SKILL.md +5 -1
  9. package/claude/skills/canon-feedback/SKILL.md +18 -8
  10. package/claude/skills/canon-feedback-triage/SKILL.md +10 -3
  11. package/claude/skills/canvas-board/SKILL.md +36 -17
  12. package/claude/skills/ci-workflow/SKILL.md +5 -4
  13. package/claude/skills/code-craft/SKILL.md +10 -6
  14. package/claude/skills/codebase-layout/SKILL.md +3 -5
  15. package/claude/skills/context-fold/REQUIREMENT.md +4 -4
  16. package/claude/skills/context-fold/SKILL.md +42 -23
  17. package/claude/skills/context-fold/references/context-refresh.md +4 -3
  18. package/claude/skills/context-fold/references/context.md +79 -64
  19. package/claude/skills/create-rule/SKILL.md +7 -3
  20. package/claude/skills/create-skill/SKILL.md +27 -7
  21. package/claude/skills/create-skill/references/skill-reference.md +41 -17
  22. package/claude/skills/decision-escalate/SKILL.md +8 -5
  23. package/claude/skills/deploy-app/SKILL.md +1 -1
  24. package/claude/skills/deprecation-migration/SKILL.md +12 -4
  25. package/claude/skills/design-taste/SKILL.md +17 -11
  26. package/claude/skills/docs-sync/REQUIREMENT.md +1 -0
  27. package/claude/skills/docs-sync/SKILL.md +17 -6
  28. package/claude/skills/docs-sync/references/docs.md +54 -21
  29. package/claude/skills/docs-sync/references/readme.md +32 -8
  30. package/claude/skills/document-health/SKILL.md +3 -4
  31. package/claude/skills/draft-and-pick/SKILL.md +73 -47
  32. package/claude/skills/draft-doc/SKILL.md +9 -4
  33. package/claude/skills/draft-doc/references/context.md +4 -4
  34. package/claude/skills/draft-doc/references/docs.md +1 -0
  35. package/claude/skills/draft-doc/references/wiki.md +3 -2
  36. package/claude/skills/draft-figure/SKILL.md +9 -7
  37. package/claude/skills/draft-identity/SKILL.md +24 -52
  38. package/claude/skills/draft-identity/references/card-route.md +46 -0
  39. package/claude/skills/draft-ready/SKILL.md +19 -7
  40. package/claude/skills/draft-screencast/SKILL.md +6 -112
  41. package/claude/skills/draft-screencast/references/beats-template.md +119 -0
  42. package/claude/skills/draft-slides/SKILL.md +7 -4
  43. package/claude/skills/git-branch/SKILL.md +3 -3
  44. package/claude/skills/git-commit/SKILL.md +5 -3
  45. package/claude/skills/git-followup/SKILL.md +16 -9
  46. package/claude/skills/git-issue/SKILL.md +6 -5
  47. package/claude/skills/git-issue/references/issue.md +11 -10
  48. package/claude/skills/git-pr/SKILL.md +56 -44
  49. package/claude/skills/git-ship/SKILL.md +43 -34
  50. package/claude/skills/git-ship/references/ci-watch.md +31 -0
  51. package/claude/skills/git-split/SKILL.md +28 -100
  52. package/claude/skills/git-split/references/independent.md +40 -0
  53. package/claude/skills/git-split/references/stacked.md +73 -0
  54. package/claude/skills/git-stage/SKILL.md +9 -7
  55. package/claude/skills/git-worktree/SKILL.md +27 -49
  56. package/claude/skills/git-worktree/references/cleanup.md +55 -0
  57. package/claude/skills/markdown-craft/SKILL.md +2 -2
  58. package/claude/skills/markdown-craft/references/markdown.md +82 -52
  59. package/claude/skills/plan-feature/SKILL.md +52 -46
  60. package/claude/skills/plan-feature/references/batches.md +46 -0
  61. package/claude/skills/plan-groundwork/SKILL.md +60 -74
  62. package/claude/skills/plan-groundwork/references/groundwork.md +53 -24
  63. package/claude/skills/plan-groundwork/references/output.md +48 -0
  64. package/claude/skills/plan-intake/SKILL.md +28 -21
  65. package/claude/skills/plan-intake-answer/SKILL.md +14 -13
  66. package/claude/skills/read-frames/SKILL.md +2 -0
  67. package/claude/skills/record-screencast/SKILL.md +31 -11
  68. package/claude/skills/review-address/SKILL.md +76 -158
  69. package/claude/skills/review-address/references/rebase-only.md +34 -0
  70. package/claude/skills/review-craft/SKILL.md +27 -11
  71. package/claude/skills/review-pr/SKILL.md +91 -40
  72. package/claude/skills/review-pr/references/post.md +1 -1
  73. package/claude/skills/review-ui/SKILL.md +62 -37
  74. package/claude/skills/review-ui/references/hosted-preview.md +30 -0
  75. package/claude/skills/role-orchestrator/SKILL.md +105 -74
  76. package/claude/skills/role-orchestrator/references/orchestrator-filing.md +26 -0
  77. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +1 -1
  78. package/claude/skills/role-planner/SKILL.md +66 -64
  79. package/claude/skills/role-reviewer/SKILL.md +57 -46
  80. package/claude/skills/role-worker/SKILL.md +68 -63
  81. package/claude/skills/search-craft/SKILL.md +7 -5
  82. package/claude/skills/session-compact/SKILL.md +16 -11
  83. package/claude/skills/session-map/SKILL.md +31 -10
  84. package/claude/skills/session-relay/SKILL.md +32 -65
  85. package/claude/skills/session-resume/SKILL.md +18 -13
  86. package/claude/skills/session-worktree/SKILL.md +70 -42
  87. package/claude/skills/systematic-debugging/SKILL.md +6 -1
  88. package/claude/skills/target-check/SKILL.md +13 -6
  89. package/claude/skills/target-setup/SKILL.md +41 -30
  90. package/claude/skills/target-setup/references/verify.md +6 -5
  91. package/claude/skills/task-board/SKILL.md +68 -60
  92. package/claude/skills/task-board/references/create-report.md +36 -0
  93. package/claude/skills/task-board/references/tasks.md +3 -1
  94. package/claude/skills/task-complete/SKILL.md +20 -18
  95. package/claude/skills/teach-workspace/SKILL.md +61 -33
  96. package/claude/skills/test-craft/SKILL.md +2 -1
  97. package/claude/skills/test-first/SKILL.md +15 -9
  98. package/claude/skills/ui-checklist/SKILL.md +18 -16
  99. package/claude/skills/ux-audit/SKILL.md +9 -10
  100. package/claude/skills/ux-measure/SKILL.md +22 -18
  101. package/claude/skills/video-craft/SKILL.md +15 -7
  102. package/claude/skills/write-human/SKILL.md +9 -1
  103. package/claude/skills/youtube-transcripts/SKILL.md +2 -1
  104. package/docs/agents/context-audit-checks.md +19 -1
  105. package/docs/agents/context-audit.md +2 -2
  106. package/docs/agents/driver.md +1 -1
  107. package/docs/agents/hooks.md +21 -1
  108. package/docs/agents/index.md +10 -14
  109. package/docs/agents/indexes.md +25 -29
  110. package/docs/agents/install-and-sync.md +124 -235
  111. package/docs/agents/markdown-audit-density.md +39 -0
  112. package/docs/agents/markdown-audit-weight.md +9 -7
  113. package/docs/agents/markdown-audit.md +18 -14
  114. package/docs/agents/migrate.md +41 -16
  115. package/docs/agents/output-shape.md +60 -45
  116. package/docs/agents/overview.md +36 -12
  117. package/docs/agents/routing.md +1 -1
  118. package/docs/agents/rule-citations.md +1 -1
  119. package/docs/agents/skills-reach.md +1 -1
  120. package/docs/agents/targets.md +25 -77
  121. package/docs/target/migrations.md +1 -1
  122. package/docs/target/projects.md +3 -3
  123. package/docs/target/scenarios.md +1 -1
  124. package/docs/workflow/ai-workflow.md +2 -2
  125. package/docs/workflow/parallel-features.md +2 -2
  126. package/docs/workflow/skill-map.md +1 -1
  127. package/docs/workflow/visual-design-workflow.md +1 -1
  128. package/governance/detect.toml +6 -1
  129. package/package.json +6 -3
  130. package/scripts/core/install-check.sh +1 -1
  131. package/src/commands/claude.ts +1 -2
  132. package/src/commands/context.ts +106 -7
  133. package/src/commands/hooks.ts +63 -0
  134. package/src/commands/markdown.ts +169 -32
  135. package/src/commands/pr.ts +1 -1
  136. package/src/commands/records.ts +0 -1
  137. package/src/context/audit.ts +256 -5
  138. package/src/context/classify/prompts.ts +1 -1
  139. package/src/context/gate.ts +23 -7
  140. package/src/context/shape-gated.ts +28 -0
  141. package/src/design/document.ts +1 -1
  142. package/src/design/tokens.ts +1 -1
  143. package/src/docs/read.ts +15 -21
  144. package/src/gate/measures.ts +15 -190
  145. package/src/gate/stages.ts +21 -23
  146. package/src/hooks/reply-links.ts +110 -0
  147. package/src/markdown/density.ts +258 -0
  148. package/src/markdown/gate.ts +57 -18
  149. package/src/markdown/reference-shape.ts +103 -0
  150. package/src/markdown/scope.ts +174 -0
  151. package/src/markdown/structure.ts +10 -60
  152. package/src/sandbox/equivalence.ts +7 -9
  153. package/src/shipped/references.ts +1 -1
  154. package/src/teach/browser/head.ts +2 -2
  155. package/src/teach/browser/sidebar.ts +3 -3
  156. package/src/teach/browser/theme.ts +2 -2
  157. package/src/tooling/inject.ts +2 -2
  158. package/src/tooling/package.ts +15 -18
  159. package/src/tooling/verify-stack.ts +6 -3
  160. package/tooling/astro/configs/.oxfmtrc.json +8 -0
  161. package/tooling/astro/configs/.prettierrc +13 -0
  162. package/tooling/astro/manifest.toml +13 -0
  163. package/tooling/astro/reference.md +15 -5
  164. package/tooling/base/configs/.oxfmtrc.json +7 -0
  165. package/tooling/base/configs/scripts/verify.ts +1 -1
  166. package/tooling/base/manifest.toml +6 -6
  167. package/tooling/base/reference.md +12 -9
  168. package/tooling/base/seeds/.lintstagedrc +1 -3
  169. package/tooling/base/seeds/canon/context/ci.md +3 -3
  170. package/tooling/base/seeds/canon/context/development.md +2 -2
  171. package/tooling/claude/manifest.toml +5 -1
  172. package/tooling/claude/seeds/.claude/hooks/reply-links.sh +72 -0
  173. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +22 -1
  174. package/tooling/claude/seeds/.claude/settings.json +4 -0
  175. package/tooling/go/reference.md +2 -2
  176. package/tooling/nextjs/configs/.oxlintrc.json +223 -0
  177. package/tooling/nextjs/manifest.toml +2 -5
  178. package/tooling/nextjs/reference.md +4 -4
  179. package/tooling/php/manifest.toml +6 -1
  180. package/tooling/php/reference.md +2 -2
  181. package/tooling/python/manifest.toml +6 -1
  182. package/tooling/python/reference.md +4 -4
  183. package/tooling/vite-react/manifest.toml +1 -4
  184. package/tooling/vite-react/reference.md +3 -3
  185. package/tooling/web/configs/.oxlintrc.json +213 -0
  186. package/tooling/web/manifest.toml +9 -13
  187. package/tooling/web/reference.md +14 -14
  188. package/docs/agents/commands.md +0 -154
  189. package/docs/agents/docs.md +0 -27
  190. package/docs/agents/drift-surfaces.md +0 -201
  191. package/docs/agents/gov-detect.md +0 -47
  192. package/docs/agents/scripting.md +0 -142
  193. package/src/markdown/growth.ts +0 -43
  194. package/tooling/base/configs/.prettierrc +0 -12
  195. package/tooling/base/seeds/.prettierignore +0 -0
  196. package/tooling/nextjs/configs/eslint.config.js +0 -91
  197. package/tooling/web/configs/eslint.config.js +0 -82
@@ -1,201 +0,0 @@
1
- ---
2
- title: Drift surfaces
3
- description: The seven sections canon sync --check reports beside the per-domain scan, being seeds, superseded artifacts, unmigrated domains, surfaces at the old root, new rules and skills, and the reverse walk, with the managed-target gate and what counts toward the exit code
4
- ---
5
-
6
- # Drift surfaces
7
-
8
- `canon sync --check` reports seven sections outside the per-domain scan that
9
- `install-and-sync.md` describes, because each names something
10
- that walk cannot see: `seeds`, `superseded`, `unmigrated`, `unmovedSurfaces`,
11
- `newSkills`, `newRules`, and `reverse`. None of them produces a change, and no
12
- sync command applies any of them. The headings below group the first four
13
- together and give `newSkills` its description under `newRules`, which answers the
14
- same question about a different corpus.
15
-
16
- All seven report only against a toolkit-managed target, which is one carrying a
17
- `.claude/` directory, a `CLAUDE.md`, or a domain still at the root layout. The
18
- report says so through `managed` in the JSON and routes an unmanaged directory to
19
- `canon init`. Seeds are why the gate exists, since they enumerate from the toolkit
20
- source rather than from what a target installed, so an unmanaged directory would
21
- otherwise report every seed as `missing`.
22
-
23
- A root-layout domain counts as a marker on its own, because the detection fires
24
- only on root files the toolkit ships and a project in the old layout is one the
25
- toolkit installed. When `managed` is false every section comes back empty rather
26
- than the render alone going quiet, so a consumer reading `--json` never acts on a
27
- finding the rendered half withheld.
28
-
29
- ## Seeds, superseded artifacts, unmigrated domains, and old-root surfaces
30
-
31
- `seeds` classifies every seed the toolkit ships against the target's copy, as
32
- `matching`, `stale`, `drifted`, or `missing`. `missing` has no per-domain
33
- equivalent, since the domain walk lists what a target installed and cannot see a
34
- file that never arrived. There is no `customized` verdict here, because that one
35
- needs a stamp and seeds carry none, so a file history cannot attribute stays
36
- `drifted`. Merge the section by hand against the toolkit copy rather than
37
- replacing a file the project edits.
38
-
39
- A markdown seed installs rewritten rather than copied, since the `stub: true`
40
- marker the toolkit's own seed gate reads is stripped on the way in. The
41
- comparison above runs against what the install would write, so a marked seed a
42
- target never touched still reports `matching`. Every other seed copies byte for
43
- byte.
44
-
45
- `superseded` names a file a newer seed folder replaced, such as `.claude/TASKS.md`
46
- against the `.canon/tasks/` that now ships. The entry carries `replacedBy` and
47
- nothing else, and the file is never deleted, since the content belongs to the
48
- project and only its author can decide where it moves. The list derives from the
49
- seed tree rather than from a fixed set of filenames, so a folder added later is
50
- covered without a code change. Only an exact stem matches, which leaves a
51
- suffixed variant such as `TASKS-ARCHIVE.md` unreported.
52
-
53
- No skill splits the file. The destination's shape lives in the skill that owns
54
- it, so the split is the project's own to make.
55
-
56
- `unmigrated` names a domain sitting at the root layout an older toolkit installed
57
- to, with nothing at the path the current one reads. It carries `rootPath`,
58
- `installPath`, and a file count. Without it a project holding an older layout at its
59
- root reports zero entries for that domain and reads as clean, which is the most
60
- misleading state the report can produce.
61
-
62
- The field currently names no domain. Snippets was the one the toolkit ever
63
- installed at a project root, and it retired outright. A leftover `snippets/`
64
- folder reports under the reverse walk instead, where `docs/target/sync.md` says
65
- what to do with it.
66
-
67
- `unmovedSurfaces` names a tracked surface the target still holds at its `.claude/`
68
- spelling with nothing at the `canon/` one, such as `.claude/REQUIREMENTS.md` or
69
- `.claude/context/`. Each entry carries `rel` and `movesTo`, and the render names
70
- `canon migrate surface-roots`, the verb that moves them. Rules glob the `canon/`
71
- spelling alone, so a target on the old root stops loading the rules scoped to
72
- those files until it moves. <!-- canon-keep-surface-root -->
73
-
74
- ## Rules the target never received
75
-
76
- `newRules` names a rule the target could receive and its tree does not hold. A
77
- sync refreshes the files a target already holds and adds none, so without this
78
- section a project's rule set freezes at its install date while every file it
79
- does hold reports as current. That is the report's most confident wrong
80
- answer, since a clean result reads as a target holding everything the toolkit
81
- publishes.
82
-
83
- The list rides beside `newSkills`, which asks the same question about the plugin
84
- catalog. Both are names rather than paths, and neither queues a change, because
85
- the two remedies differ: a skill loads live from the plugin directory and needs
86
- nothing run, while a rule reaches a target only when someone runs
87
- `canon gov install`.
88
-
89
- Read a clean section as reporting rather than as delivering. Nothing here
90
- installs, nothing counts toward `--exit-code`, and a target can read the list and
91
- act on none of it. The value stops at an operator reading it, which is the same
92
- contract the skills list already sets.
93
-
94
- Since `canon gov install` records the stack it resolved, a target carrying that
95
- record answers this by comparing its current entitlement against its current
96
- tree, with no anchor and no git diff involved. That is what lets the section
97
- name a rule that shipped before the target's last sync: the anchor a sync
98
- advances plays no part in the read, where an anchor-bound diff can never see a
99
- rule on the far side of a window a later sync moved past it. The per-file
100
- `missing` state `canon gov sync` reports comes from the same comparison, so a
101
- rule the chain lists reaches both surfaces the same way.
102
-
103
- A target stamped before governance recorded a chain falls back to the older
104
- rule-folder-inference read below. The measurement there anchors on governance's own
105
- stamp rather than on the oldest anchor across domains, since rules are
106
- domain-scoped and a shared anchor would let another domain's sync move the
107
- revision rules are measured from. A target carrying no chain and no governance anchor
108
- reports nothing at all: it has no date to measure against, and diffing from the
109
- start of history would read the whole catalog as new.
110
-
111
- An anchor this toolkit cannot resolve reports nothing by the same route, and that
112
- one is not visible. A stamp naming a revision the running clone has never seen,
113
- which is what a registry install without history or a shallow clone produces,
114
- fails the read and yields an empty list rather than a stated absence. It looks
115
- identical to a target holding every rule the toolkit publishes. `newSkills`
116
- behaves the same way, and neither carries the `historyUnavailable` flag the
117
- per-domain scan uses to separate the two. Treat an empty section on a toolkit
118
- that is not a full clone as unmeasured rather than clean. This gap does not
119
- reach the chain-based read above, since it consults no anchor at all.
120
-
121
- In the fallback, entitlement is filtered, because a stack does not receive
122
- every rule. The base stack names ten rules, the language stacks add `code` and
123
- their own language and testing rules, and every other rule is opt-in, so an
124
- unfiltered list would tell a base consumer about rules it can never receive and
125
- train the reader to skip the section.
126
-
127
- The filter accepts a rule folder on either of two grounds. A folder holding a rule the
128
- base stack resolves to is entitled to every target, read through the stack file so
129
- a rule added to base later needs no code change. Every other rule folder is read off the folders the
130
- target already carries, which is the fallback's only evidence of what a target
131
- was entitled to before it recorded a chain. One rule folder can be reached by more than
132
- one stack, so the test over-reports inside a folder the target holds, which
133
- costs a line where under-reporting would cost the section its point.
134
-
135
- A rule the target already holds is dropped by name. That is what keeps a rule the
136
- toolkit moved between rule folders out of the list, since a rename reaches this
137
- read as an addition and only the name tells the two apart.
138
-
139
- This closes the dangling-citation case as a side effect rather than checking for
140
- one. The live instance is a rule citing a sibling authored after the target's
141
- install, and the section names the sibling as new rather than naming the citation
142
- as broken. That is enough for an operator to act on, and it is not a citation
143
- check.
144
-
145
- ## The reverse walk
146
-
147
- `reverse` is the one section built by walking the target rather than the
148
- catalog. Every other surface enumerates toolkit-owned keys and asks whether the
149
- target matches, so a folder the toolkit deleted appears in none of them. It
150
- carries `unclaimed` and `historyUnavailable`.
151
-
152
- `unclaimed` names a folder the target holds at a top-level path the toolkit once
153
- shipped and has since deleted. The candidate roots come from the toolkit's own
154
- history rather than from a list, so a root dropped later is covered without a
155
- code change. Scoping to those roots is what keeps the walk useful: walking the
156
- whole tree reports every project folder as unclaimed, which is true and says
157
- nothing.
158
-
159
- The managed gate above applies here too, and it is the one place it surprises.
160
- A directory holding a dropped folder and nothing else reports an empty `reverse`
161
- rather than the folder, because it carries none of the three markers. Read an
162
- empty section on an unmanaged target as a walk that never ran rather than as a
163
- clean result.
164
-
165
- Each entry carries `rel`, a file count, and an `attribution` of `dropped`,
166
- `project`, or `unattributed`. A dropped folder and one the project wrote are the
167
- same bytes at the same path, so the verdict is traced from history rather than
168
- guessed from the filesystem.
169
-
170
- Content matching a version the toolkit published reads as `dropped` and carries
171
- the `since` commit that published it. Names the toolkit shipped holding content
172
- it never published read as `unattributed`, which is a state in its own right
173
- rather than a soft yes. No overlap at all reads as `project`, and the render
174
- drops those while the JSON keeps them.
175
-
176
- Only files whose path the toolkit once held are hashed, so a project folder
177
- colliding on a retired name costs the walk no reads. The cost is that a file the
178
- toolkit shipped and the target renamed goes unmatched, the same limit the
179
- `unmigrated` count carries.
180
-
181
- ## What counts toward the gate
182
-
183
- `unmigrated` counts toward `--exit-code`, since running the relocation closes it.
184
- `superseded` and every seed state are excluded, for the reason `orphaned` already
185
- is: only the user can move content they wrote, so failing a job on it leaves the
186
- job red with no mechanical remedy.
187
-
188
- `unmovedSurfaces` is excluded although `canon migrate surface-roots` closes it.
189
- The move rewrites citations across the project, so it is a change the project
190
- schedules, and a job counting it would go red the day a target updated the CLI.
191
-
192
- `newRules` is excluded on a different ground, since a command does close it. What
193
- excludes it is that installing a rule changes what a project is governed by, so
194
- gating on the count would pressure a target into adopting rules nobody chose.
195
- `newSkills` is excluded because it needs no command at all.
196
-
197
- The whole `reverse` section is excluded on the same grounds, and more strongly.
198
- Every entry in it is a judgment about a file the project may own, and one of its
199
- three verdicts is a labelled unknown by design. The unmigrated detection shipped
200
- that exact false positive once, failing a push with no action that cleared it,
201
- so this section reports and gates nothing.
@@ -1,47 +0,0 @@
1
- ---
2
- title: Rule detection
3
- description: Proposing opt-in governance rules from a target's dependencies and paths, the evidence file the verb reads, the record it emits, and its exit codes
4
- ---
5
-
6
- # Rule detection
7
-
8
- `canon gov detect` proposes the opt-in governance rules a target's evidence points at, and writes nothing. A rule no stack names reaches a project only through this verb, which `canon:target-setup` calls before it previews an install.
9
-
10
- ```bash
11
- canon gov detect
12
- canon gov detect ../my-app --stack node --json
13
- ```
14
-
15
- | Option | Behavior |
16
- | ----------------- | ------------------------------------------------ |
17
- | `--stack <stack>` | Leave out every rule this stack already resolves |
18
- | `--json` | Emit the record on stdout instead of the frame |
19
-
20
- ## Evidence
21
-
22
- Each opt-in rule keeps a table in the toolkit's `governance/detect.toml`, keyed by rule basename. Its `deps` are package names, read from `package.json` (`dependencies` and `devDependencies`), `pyproject.toml`, `go.mod`, and `composer.json`. Its `files` are globs matched against the target's git-listed paths and against each folder on those paths, so `.github/workflows` matches a folder holding a workflow. A glob with no wildcard is also tested on disk, which is how `.canon/canvas` matches in a target that gitignores `.canon/`.
23
-
24
- Both reads stop at depth two, which reaches a service folder and a workspace package under `packages/<name>/`. Outside git the verb walks the directory to the same depth and skips `.git`, `node_modules`, `vendor`, and `.venv`. A manifest that does not parse contributes nothing.
25
-
26
- ## Record
27
-
28
- ```json
29
- {
30
- "target": "/abs/path/my-app",
31
- "stack": "node",
32
- "proposals": [
33
- {
34
- "rule": "react",
35
- "evidence": [{ "kind": "dep", "match": "react", "path": "package.json" }]
36
- }
37
- ]
38
- }
39
- ```
40
-
41
- `kind` is `dep` for a dependency, with `path` naming the manifest that declares it, or `file` for a glob, with `path` naming what it matched. `stack` is null when no `--stack` was passed.
42
-
43
- ## Exit codes
44
-
45
- The verb exits 0 on every readable target, including one with no evidence, which returns an empty `proposals` array. A target that does not exist and a `--stack` that does not resolve each exit 1 with the reason on stderr.
46
-
47
- Install what the operator kept with `canon gov install --add <rules> <target>`, which needs no stack.
@@ -1,142 +0,0 @@
1
- ---
2
- title: Scripting
3
- description: The runtime catalogs that replace hardcoded names, what each carries, and a headless invocation per domain
4
- ---
5
-
6
- # Scripting
7
-
8
- What a skill or script reads to discover names at runtime, and how each domain is invoked with no TTY.
9
-
10
- ## Runtime catalogs
11
-
12
- Use these to discover what's available instead of hardcoding names.
13
-
14
- | Command | Returns |
15
- | --------------------------------- | ---------------------------------------------------------------- |
16
- | `canon tooling list --json` | Stacks, extends chain, dep and script counts |
17
- | `canon gov list --json` | Governance stacks, rule sets, evidence, and unreferenced rules |
18
- | `canon gov detect --json` | Opt-in rules a target's dependencies and paths propose |
19
- | `canon claude seeds list --json` | Seed doc sources with content |
20
- | `canon claude skills list --json` | Plugin skills, descriptions, requirement flag, family vocabulary |
21
- | `canon docs list --json` | Consumer docs plus per-domain context |
22
- | `canon audits list --json` | Audits the set runs, the corpus each reads, and whether it gates |
23
-
24
- ### Catalog fields
25
-
26
- Every catalog serializes through `JSON.stringify`, so a name carrying a quote
27
- emits valid JSON. `canon tooling list` previously built
28
- its output with `printf` and no escaping.
29
-
30
- `canon claude seeds list` reads the same plan `canon claude init` applies, so the
31
- listing and the install cannot disagree. It now reports
32
- `canon/context/index.md`, which `init` has always installed and the listing
33
- never named, and it emits the project-level `CLAUDE.md` last rather than first.
34
-
35
- `canon gov list` reports each stack's rules already expanded. A stack entry names
36
- either a rule or a whole rule folder under `governance/rules/`, and the folder
37
- form reaches this listing as the rules it stands for rather than as the folder
38
- name. A consumer deduping `--add` extras against a stack therefore reads the same
39
- set the install writes, whichever form the stack file uses.
40
-
41
- It also carries `unreferenced`, the rules no stack reaches, on every invocation
42
- rather than behind a flag. These install only through `--add`, so the key answers
43
- what a stack leaves out without a caller resolving every stack itself. The key is
44
- present alongside `stacks` and `rules` and survives `--stacks` or `--rules`
45
- narrowing the other two.
46
-
47
- ### The skills catalog
48
-
49
- `canon claude skills list` reads `claude/skills/*/SKILL.md` and reports the folder
50
- name with the frontmatter description, sorted by name. Internal skills under
51
- `.claude/skills/` are excluded, since they never install into a target and a
52
- count spanning both overstates what ships. A skill whose frontmatter is missing
53
- or unparseable returns an empty description rather than failing the listing, so
54
- one malformed file cannot hide the rest of the catalog. `--names` emits skill
55
- names one per line.
56
-
57
- Each skill in `--json` carries `family`, the `metadata.family` its frontmatter
58
- declares, or `null` where it declares none. A top-level `families` array holds
59
- the vocabulary as `{ key, group }` entries in skill map order, where `group` is
60
- the map heading the family's rows sit under. Read a new skill's family from it
61
- rather than from the map. `--by-family` prints the human listing as one step per
62
- family, with a trailing step for any skill whose family is missing or unknown.
63
-
64
- Each entry also carries `requirement`, whether the folder holds a sibling
65
- `REQUIREMENT.md`. Every skill is meant to carry one, so a `false` is a gap to
66
- close rather than a recorded exemption, and the flag answers which skills are
67
- missing theirs without a caller listing the directory itself. `canon claude skills
68
- audit` gates that rule across both corpora, so read this flag for the state of
69
- the shipped catalog and the audit for whether a working tree conforms. Its
70
- surface is in `skills-audit.md`.
71
-
72
- ## Non-interactive examples
73
-
74
- ```bash
75
- # Report what a stack would change, writing nothing
76
- CANON_NON_INTERACTIVE=1 canon tooling diff astro /path/to/project
77
-
78
- # Sync a stack into a target project, overwriting its golden configs
79
- CANON_NON_INTERACTIVE=1 canon tooling sync astro /path/to/project --write
80
-
81
- # Install a governance stack (the stack argument is required headlessly)
82
- CANON_NON_INTERACTIVE=1 canon gov install node --add astro,shadcn /path/to/project
83
-
84
- # Update installed governance rules, dropping a retired .claude/GOV.md
85
- CANON_NON_INTERACTIVE=1 canon gov sync /path/to/project
86
-
87
- # Concatenate installed rules into a paste payload
88
- CANON_NON_INTERACTIVE=1 canon gov build /path/to/project
89
-
90
- # Sync a monorepo subtree, skipping the base layer the repo root already owns
91
- CANON_NON_INTERACTIVE=1 canon tooling sync vite-react /path/to/repo/frontend --skip base --write
92
-
93
- # Verify a stack end-to-end in a throwaway scaffold
94
- canon tooling verify vite-react
95
-
96
- # Apply one stack without scanning or prompting, for scripted provisioning
97
- canon tooling inject base /path/to/project
98
- canon tooling inject base /path/to/project --configs --seeds
99
-
100
- # Drop managed gitignore entries a manifest no longer declares
101
- # Prints the number removed on stdout, diagnostics on stderr
102
- canon tooling prune-gitignore base /path/to/project
103
-
104
- # Bootstrap a project. Any flag suppresses the confirmation prompt
105
- CANON_NON_INTERACTIVE=1 canon init --stack node --skip wiki /path/to/project
106
-
107
- # Run every domain sync. The git workflow is refused headlessly, so nothing is pushed
108
- CANON_NON_INTERACTIVE=1 canon sync /path/to/project
109
-
110
- # Scaffold .claude/wiki/ with a stub index. The target must already exist
111
- CANON_NON_INTERACTIVE=1 canon wiki init /path/to/project
112
-
113
- # Run a sandbox scenario non-interactively
114
- SANDBOX_SCENARIO=sync canon sandbox infra:tooling
115
-
116
- # Read every audit as one record. Exit 2 is a fact, 3 an audit that did not report
117
- canon audits run --json
118
-
119
- # Read committed state rather than an arriving change. Exit 2 carries findings
120
- canon secrets scan --json
121
- canon deps audit --json
122
- ```
123
-
124
- The two state-scoped verbs both exit 2 on findings and mean different things by
125
- it. A credential in the shipped tree is a fact and fails `canon audits run`, while
126
- a published advisory is a judgment that reports and moves no verdict. Neither
127
- reads a diff, so a consumer already running a review surface gets no overlap.
128
-
129
- Both refuse rather than report clean when they have no corpus, and the refusal
130
- reason is the field to branch on. A project publishing nothing refuses the secret
131
- scan with `no-manifest`, and one whose dependencies are not installed refuses the
132
- advisory check with `no-lockfile`. The aggregate reads those as an absent corpus
133
- rather than a broken run, so neither pins its verdict at `incomplete`.
134
-
135
- `canon tooling sync` is the one verb above whose flag is mandatory headlessly. It
136
- overwrites every golden config a stack ships, which reaches the CI workflow, the
137
- git hooks, the end-to-end harness, and the shell scripts under `scripts/`, so a
138
- headless run carrying neither `--check` nor `--write` reports what it would
139
- replace and exits 1 rather than applying it. Run `--check` first to read the
140
- list, then `--write` to apply it. `canon tooling sync --help` names both, and
141
- `canon tooling diff <stack> <target>` reports the per-stack path list resolved
142
- against a real target.
@@ -1,43 +0,0 @@
1
- import { bodyLines } from '@/markdown/scan'
2
- import {
3
- CHECKPOINTS,
4
- type Checkpoints,
5
- type LongBlock,
6
- longBlocks,
7
- } from '@/markdown/structure'
8
-
9
- /**
10
- * Reduces text to the words a reader meets, so a block reflowed, re-marked, or
11
- * moved under another heading still matches the copy it came from.
12
- *
13
- * List markers and emphasis marks are dropped because turning a bullet into a
14
- * labelled bullet or a paragraph changes the shape of a statement and none of
15
- * its words, which is the edit the sweep makes. A word changed is a text
16
- * changed, and that is what the growth stage exists to see.
17
- */
18
- export function normalizeProse(text: string): string {
19
- return text
20
- .replace(/^\s*(?:[-*+]|\d+\.)\s+/gm, '')
21
- .replace(/[*_]/g, '')
22
- .replace(/\s+/g, ' ')
23
- .trim()
24
- }
25
-
26
- /**
27
- * The paragraphs and top-level bullets of `source` past their checkpoints whose
28
- * text appears nowhere in `base`.
29
- *
30
- * Containment rather than line matching, so an unchanged long block passes
31
- * wherever it sits and an edited or new one fails. `base` is every document the
32
- * caller counts as the copy it may have come from, already normalized, since a
33
- * block moved into a sibling entry is as unchanged as one left in place.
34
- */
35
- export function grownBlocks(
36
- source: string,
37
- base: string,
38
- checkpoints: Checkpoints = CHECKPOINTS,
39
- ): LongBlock[] {
40
- return longBlocks(bodyLines(source), checkpoints).filter(
41
- (block) => !base.includes(normalizeProse(block.text)),
42
- )
43
- }
@@ -1,12 +0,0 @@
1
- {
2
- "semi": false,
3
- "singleQuote": true,
4
- "overrides": [
5
- {
6
- "files": "*.mdc",
7
- "options": {
8
- "parser": "markdown"
9
- }
10
- }
11
- ]
12
- }
File without changes
@@ -1,91 +0,0 @@
1
- import js from '@eslint/js'
2
- import { defineConfig, globalIgnores } from 'eslint/config'
3
- import prettier from 'eslint-config-prettier'
4
- import checkFile from 'eslint-plugin-check-file'
5
- import reactHooks from 'eslint-plugin-react-hooks'
6
- import reactRefresh from 'eslint-plugin-react-refresh'
7
- import simpleImportSort from 'eslint-plugin-simple-import-sort'
8
- import vitest from 'eslint-plugin-vitest'
9
- import globals from 'globals'
10
- import tseslint from 'typescript-eslint'
11
-
12
- export default defineConfig([
13
- globalIgnores([
14
- '.next',
15
- 'next-env.d.ts',
16
- 'dist',
17
- 'dist-ssr',
18
- 'coverage',
19
- 'release',
20
- '.claude',
21
- '.vscode',
22
- '.husky',
23
- 'test-results',
24
- 'playwright-report',
25
- 'blob-report',
26
- 'playwright/.cache',
27
- ]),
28
- js.configs.recommended,
29
- ...tseslint.configs.recommended,
30
- {
31
- files: ['**/*.{ts,tsx,js,jsx}'],
32
- plugins: {
33
- 'react-hooks': reactHooks,
34
- 'react-refresh': reactRefresh,
35
- 'simple-import-sort': simpleImportSort,
36
- 'check-file': checkFile,
37
- },
38
- languageOptions: {
39
- globals: {
40
- ...globals.browser,
41
- },
42
- },
43
- rules: {
44
- ...reactHooks.configs.recommended.rules,
45
- 'react-refresh/only-export-components': [
46
- 'warn',
47
- { allowConstantExport: true },
48
- ],
49
- 'simple-import-sort/imports': 'error',
50
- 'simple-import-sort/exports': 'error',
51
- '@typescript-eslint/no-unused-vars': [
52
- 'error',
53
- { varsIgnorePattern: '^_', argsIgnorePattern: '^_' },
54
- ],
55
- 'check-file/filename-naming-convention': [
56
- 'error',
57
- { '**/*.{ts,tsx}': 'KEBAB_CASE' },
58
- { ignoreMiddleExtensions: true },
59
- ],
60
- 'check-file/folder-naming-convention': [
61
- 'error',
62
- { 'src/**/!(__tests__)/': 'KEBAB_CASE' },
63
- ],
64
- },
65
- },
66
- {
67
- // App Router route and layout files export non-component values (metadata, route handlers) and use bracket, paren, and at-sign folder syntax (dynamic segments, route groups, parallel slots), which these rules flag as violations.
68
- files: ['src/app/**/*.{ts,tsx}'],
69
- rules: {
70
- 'react-refresh/only-export-components': 'off',
71
- 'check-file/folder-naming-convention': 'off',
72
- },
73
- },
74
- {
75
- files: ['**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}'],
76
- ...vitest.configs.recommended,
77
- },
78
- {
79
- files: [
80
- '*.config.{js,mjs,cjs,ts}',
81
- 'vitest.config.ts',
82
- 'playwright.config.ts',
83
- ],
84
- languageOptions: {
85
- globals: {
86
- ...globals.node,
87
- },
88
- },
89
- },
90
- prettier,
91
- ])
@@ -1,82 +0,0 @@
1
- import js from '@eslint/js'
2
- import { defineConfig, globalIgnores } from 'eslint/config'
3
- import prettier from 'eslint-config-prettier'
4
- import checkFile from 'eslint-plugin-check-file'
5
- import reactHooks from 'eslint-plugin-react-hooks'
6
- import reactRefresh from 'eslint-plugin-react-refresh'
7
- import simpleImportSort from 'eslint-plugin-simple-import-sort'
8
- import vitest from 'eslint-plugin-vitest'
9
- import globals from 'globals'
10
- import tseslint from 'typescript-eslint'
11
-
12
- export default defineConfig([
13
- globalIgnores([
14
- 'dist',
15
- 'dist-ssr',
16
- 'coverage',
17
- 'release',
18
- '.claude',
19
- '.vscode',
20
- '.husky',
21
- 'test-results',
22
- 'playwright-report',
23
- 'blob-report',
24
- 'playwright/.cache',
25
- ]),
26
- js.configs.recommended,
27
- ...tseslint.configs.recommended,
28
- {
29
- files: ['**/*.{ts,tsx,js,jsx}'],
30
- plugins: {
31
- 'react-hooks': reactHooks,
32
- 'react-refresh': reactRefresh,
33
- 'simple-import-sort': simpleImportSort,
34
- 'check-file': checkFile,
35
- },
36
- languageOptions: {
37
- globals: {
38
- ...globals.browser,
39
- },
40
- },
41
- rules: {
42
- ...reactHooks.configs.recommended.rules,
43
- 'react-refresh/only-export-components': [
44
- 'warn',
45
- { allowConstantExport: true },
46
- ],
47
- 'simple-import-sort/imports': 'error',
48
- 'simple-import-sort/exports': 'error',
49
- '@typescript-eslint/no-unused-vars': [
50
- 'error',
51
- { varsIgnorePattern: '^_', argsIgnorePattern: '^_' },
52
- ],
53
- 'check-file/filename-naming-convention': [
54
- 'error',
55
- { '**/!(App).{ts,tsx}': 'KEBAB_CASE' },
56
- { ignoreMiddleExtensions: true },
57
- ],
58
- 'check-file/folder-naming-convention': [
59
- 'error',
60
- { 'src/**/!(__tests__)/': 'KEBAB_CASE' },
61
- ],
62
- },
63
- },
64
- {
65
- files: ['**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}'],
66
- ...vitest.configs.recommended,
67
- },
68
- {
69
- files: [
70
- '*.config.{js,mjs,cjs,ts}',
71
- 'vite.config.ts',
72
- 'vitest.config.ts',
73
- 'playwright.config.ts',
74
- ],
75
- languageOptions: {
76
- globals: {
77
- ...globals.node,
78
- },
79
- },
80
- },
81
- prettier,
82
- ])