@erclx/canon 5.38.0 → 5.40.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 (246) hide show
  1. package/README.md +5 -5
  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 +37 -18
  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 +9 -7
  43. package/claude/skills/draft-slides/references/deck.md +183 -0
  44. package/claude/skills/git-branch/SKILL.md +3 -3
  45. package/claude/skills/git-commit/SKILL.md +5 -3
  46. package/claude/skills/git-followup/SKILL.md +16 -9
  47. package/claude/skills/git-issue/SKILL.md +6 -5
  48. package/claude/skills/git-pr/SKILL.md +56 -44
  49. package/claude/skills/git-pr/references/evidence.md +2 -2
  50. package/claude/skills/git-pr/references/final-command.md +1 -1
  51. package/claude/skills/git-pr/references/labels.md +1 -1
  52. package/claude/skills/git-pr/references/publish.md +1 -1
  53. package/claude/skills/git-ship/SKILL.md +43 -34
  54. package/claude/skills/git-ship/references/ci-watch.md +31 -0
  55. package/claude/skills/git-split/SKILL.md +28 -100
  56. package/claude/skills/git-split/references/independent.md +40 -0
  57. package/claude/skills/git-split/references/stacked.md +73 -0
  58. package/claude/skills/git-stage/SKILL.md +9 -7
  59. package/claude/skills/git-worktree/SKILL.md +27 -49
  60. package/claude/skills/git-worktree/references/cleanup.md +55 -0
  61. package/claude/skills/markdown-craft/SKILL.md +2 -2
  62. package/claude/skills/markdown-craft/references/markdown.md +82 -53
  63. package/claude/skills/plan-feature/SKILL.md +52 -46
  64. package/claude/skills/plan-feature/references/batches.md +46 -0
  65. package/claude/skills/plan-groundwork/SKILL.md +60 -74
  66. package/claude/skills/plan-groundwork/references/output.md +48 -0
  67. package/claude/skills/plan-intake/SKILL.md +28 -21
  68. package/claude/skills/plan-intake-answer/SKILL.md +14 -13
  69. package/claude/skills/read-frames/SKILL.md +2 -0
  70. package/claude/skills/record-screencast/SKILL.md +31 -11
  71. package/claude/skills/review-address/SKILL.md +76 -158
  72. package/claude/skills/review-address/references/rebase-only.md +34 -0
  73. package/claude/skills/review-craft/SKILL.md +27 -11
  74. package/claude/skills/review-pr/SKILL.md +91 -40
  75. package/claude/skills/review-ui/SKILL.md +63 -37
  76. package/claude/skills/review-ui/references/hosted-preview.md +30 -0
  77. package/claude/skills/role-orchestrator/SKILL.md +105 -74
  78. package/claude/skills/role-orchestrator/references/orchestrator-filing.md +26 -0
  79. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
  80. package/claude/skills/role-planner/SKILL.md +66 -64
  81. package/claude/skills/role-reviewer/SKILL.md +57 -46
  82. package/claude/skills/role-worker/SKILL.md +68 -63
  83. package/claude/skills/search-craft/SKILL.md +7 -5
  84. package/claude/skills/session-compact/SKILL.md +16 -11
  85. package/claude/skills/session-map/SKILL.md +31 -10
  86. package/claude/skills/session-relay/SKILL.md +32 -65
  87. package/claude/skills/session-resume/SKILL.md +18 -13
  88. package/claude/skills/session-worktree/SKILL.md +70 -42
  89. package/claude/skills/systematic-debugging/SKILL.md +6 -1
  90. package/claude/skills/target-check/SKILL.md +13 -6
  91. package/claude/skills/target-setup/SKILL.md +39 -27
  92. package/claude/skills/task-board/SKILL.md +68 -60
  93. package/claude/skills/task-board/references/board.md +1 -1
  94. package/claude/skills/task-board/references/create-report.md +36 -0
  95. package/claude/skills/task-board/references/tasks.md +1 -1
  96. package/claude/skills/task-complete/SKILL.md +20 -18
  97. package/claude/skills/teach-workspace/SKILL.md +61 -33
  98. package/claude/skills/test-craft/SKILL.md +2 -1
  99. package/claude/skills/test-first/SKILL.md +15 -9
  100. package/claude/skills/ui-checklist/SKILL.md +18 -16
  101. package/claude/skills/ux-audit/SKILL.md +9 -10
  102. package/claude/skills/ux-measure/SKILL.md +23 -19
  103. package/claude/skills/video-craft/SKILL.md +15 -7
  104. package/claude/skills/write-human/SKILL.md +9 -1
  105. package/claude/skills/youtube-transcripts/SKILL.md +2 -1
  106. package/docs/agents/audits.md +61 -60
  107. package/docs/agents/context-audit.md +40 -58
  108. package/docs/agents/index.md +11 -61
  109. package/docs/agents/indexes.md +25 -29
  110. package/docs/agents/install-and-sync.md +125 -235
  111. package/docs/agents/markdown-audit.md +54 -96
  112. package/docs/agents/migrate.md +41 -16
  113. package/docs/agents/output-shape.md +60 -45
  114. package/docs/agents/overview.md +36 -12
  115. package/docs/agents/records.md +37 -79
  116. package/docs/agents/sessions-transfer.md +26 -70
  117. package/docs/agents/targets.md +25 -77
  118. package/docs/target/index.md +0 -1
  119. package/docs/target/migrations.md +57 -66
  120. package/docs/target/projects.md +36 -78
  121. package/docs/target/scenarios.md +9 -19
  122. package/docs/target/sync.md +27 -68
  123. package/docs/workflow/ai-workflow.md +22 -68
  124. package/docs/workflow/operating-model.md +32 -186
  125. package/docs/workflow/parallel-features.md +29 -66
  126. package/docs/workflow/skill-map-on-request.md +22 -24
  127. package/docs/workflow/skill-map.md +72 -76
  128. package/docs/workflow/visual-design-workflow.md +25 -118
  129. package/docs/workflow/zshrc-aliases.md +18 -37
  130. package/package.json +1 -1
  131. package/src/canvas/client/inspector/fields.css +4 -0
  132. package/src/canvas/index.html +6 -0
  133. package/src/claude/skills-reach.ts +2 -2
  134. package/src/cli/ui.ts +3 -1
  135. package/src/commands/canvas.ts +23 -0
  136. package/src/commands/capture.ts +14 -0
  137. package/src/commands/claude.ts +14 -1
  138. package/src/commands/context.ts +106 -7
  139. package/src/commands/demo.ts +22 -0
  140. package/src/commands/deps.ts +6 -0
  141. package/src/commands/driver.ts +28 -2
  142. package/src/commands/gov.ts +52 -18
  143. package/src/commands/hooks.ts +63 -0
  144. package/src/commands/intake.ts +17 -0
  145. package/src/commands/labels.ts +38 -5
  146. package/src/commands/markdown.ts +169 -32
  147. package/src/commands/pr.ts +160 -43
  148. package/src/commands/records.ts +8 -0
  149. package/src/commands/secrets.ts +6 -0
  150. package/src/commands/sessions.ts +8 -0
  151. package/src/commands/tasks.ts +52 -0
  152. package/src/commands/teach.ts +13 -0
  153. package/src/commands/upstream.ts +25 -1
  154. package/src/commands/worktrees.ts +5 -0
  155. package/src/context/audit.ts +274 -5
  156. package/src/context/classify/prompts.ts +1 -1
  157. package/src/context/folders.ts +1 -1
  158. package/src/context/gate.ts +23 -7
  159. package/src/context/shape-gated.ts +30 -0
  160. package/src/counts/scan.ts +1 -1
  161. package/src/design/components.ts +1 -1
  162. package/src/design/document.ts +1 -1
  163. package/src/design/tokens.ts +1 -1
  164. package/src/gate/boundaries.ts +4 -1
  165. package/src/gate/measures.ts +16 -191
  166. package/src/gate/stages.ts +14 -30
  167. package/src/git/env.ts +6 -0
  168. package/src/hooks/reply-links.ts +110 -0
  169. package/src/markdown/density.ts +258 -0
  170. package/src/markdown/gate.ts +57 -18
  171. package/src/markdown/reference-shape.ts +103 -0
  172. package/src/markdown/scope.ts +162 -0
  173. package/src/markdown/structure.ts +9 -59
  174. package/src/pr/lift.ts +153 -19
  175. package/src/pr/painted.ts +87 -0
  176. package/src/roots/record.ts +18 -1
  177. package/src/sandbox/census.ts +1 -1
  178. package/src/sandbox/headless/run.ts +3 -2
  179. package/src/sandbox/headless/watch.ts +3 -4
  180. package/src/shipped/references.ts +1 -1
  181. package/src/slides/convert/master.ts +3 -0
  182. package/src/sync/format.ts +46 -0
  183. package/src/sync/stamp.ts +6 -0
  184. package/tooling/astro/reference.md +2 -2
  185. package/tooling/base/reference.md +1 -1
  186. package/tooling/claude/seeds/.claude/hooks/reply-links.sh +72 -0
  187. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +22 -1
  188. package/tooling/claude/seeds/.claude/settings.json +4 -0
  189. package/tooling/go/reference.md +1 -1
  190. package/tooling/nextjs/reference.md +1 -1
  191. package/tooling/php/reference.md +1 -1
  192. package/tooling/python/reference.md +1 -1
  193. package/tooling/vite-react/reference.md +1 -1
  194. package/tooling/web/reference.md +1 -1
  195. package/docs/agents/canvas.md +0 -86
  196. package/docs/agents/capture.md +0 -65
  197. package/docs/agents/census.md +0 -23
  198. package/docs/agents/commands.md +0 -154
  199. package/docs/agents/comments.md +0 -34
  200. package/docs/agents/context-audit-checks.md +0 -90
  201. package/docs/agents/context-classify.md +0 -101
  202. package/docs/agents/counts.md +0 -74
  203. package/docs/agents/demo.md +0 -140
  204. package/docs/agents/docs.md +0 -27
  205. package/docs/agents/drift-surfaces.md +0 -201
  206. package/docs/agents/driver.md +0 -103
  207. package/docs/agents/gate.md +0 -90
  208. package/docs/agents/gov-detect.md +0 -47
  209. package/docs/agents/hooks.md +0 -58
  210. package/docs/agents/intake.md +0 -81
  211. package/docs/agents/key-changes.md +0 -116
  212. package/docs/agents/label-coverage.md +0 -86
  213. package/docs/agents/markdown-audit-weight.md +0 -81
  214. package/docs/agents/pr-evidence-layout.md +0 -70
  215. package/docs/agents/pr-evidence.md +0 -273
  216. package/docs/agents/pr-frames.md +0 -131
  217. package/docs/agents/pr-lift.md +0 -132
  218. package/docs/agents/pr-local.md +0 -111
  219. package/docs/agents/pr-preview.md +0 -155
  220. package/docs/agents/pr-reads.md +0 -111
  221. package/docs/agents/pr-tick.md +0 -72
  222. package/docs/agents/records-push.md +0 -61
  223. package/docs/agents/records-stale.md +0 -40
  224. package/docs/agents/records-validate.md +0 -57
  225. package/docs/agents/restated.md +0 -83
  226. package/docs/agents/routing.md +0 -61
  227. package/docs/agents/rule-citations.md +0 -90
  228. package/docs/agents/sandbox.md +0 -126
  229. package/docs/agents/scripting.md +0 -142
  230. package/docs/agents/sessions.md +0 -128
  231. package/docs/agents/skills-audit.md +0 -120
  232. package/docs/agents/skills-reach.md +0 -64
  233. package/docs/agents/slides.md +0 -176
  234. package/docs/agents/state-scoped-risk.md +0 -105
  235. package/docs/agents/superseded.md +0 -85
  236. package/docs/agents/tasks-archive.md +0 -83
  237. package/docs/agents/tasks-plans.md +0 -152
  238. package/docs/agents/tasks-validate.md +0 -133
  239. package/docs/agents/tasks.md +0 -123
  240. package/docs/agents/teach-authoring.md +0 -118
  241. package/docs/agents/teach.md +0 -157
  242. package/docs/agents/test-order.md +0 -58
  243. package/docs/agents/upstream.md +0 -94
  244. package/docs/agents/worktrees.md +0 -118
  245. package/docs/target/standards-moved.md +0 -34
  246. package/src/markdown/growth.ts +0 -43
@@ -1,246 +1,136 @@
1
1
  ---
2
2
  title: Install and sync
3
- description: What each install and sync verb writes, refuses, or leaves alone, and how drift is attributed in a target project
3
+ description: What each install and sync verb writes, refuses, or leaves alone, the drift report and its seven side sections, the rule detection record, and version skew states
4
4
  ---
5
5
 
6
6
  # Install and sync
7
7
 
8
- The behavior notes behind the verbs listed in `commands.md`. Each one records what the verb writes, what it refuses, and what it deliberately leaves alone.
9
-
10
- ## Domain sync
11
-
12
- `canon gov sync` updates only rules already present under
13
- `.claude/rules/canon/` and never adds new ones. That wrapper marks the
14
- toolkit-owned half of the tree, and it is the whole of what the walk reaches:
15
- `.claude/rules/project/` sits outside it by location and is never read,
16
- matched, or reported on, which is how project-authored rules survive. It also
17
- removes a stale `.claude/GOV.md` from the retired build. Use
18
- `canon gov install` to add rules.
19
-
20
- Everything under `.claude/rules/canon/` belongs to the toolkit, so a rule
21
- there that the toolkit no longer ships is deleted on sync, edited or not. That
22
- is how a retired rule stops loading in a target. A renamed rule
23
- the toolkit declares in `governance/renames.toml` moves instead: the sync
24
- installs the rule under its new name and deletes the old file, whether or not
25
- the target's stamp records a stack. Local edits to the old file are not
26
- carried, and the sync says so. The sync lists each change by path before it
27
- applies, and the target's git diff is the record afterwards. `canon sync
28
- --check` lists the same file as `retired` or `renamed` before any sync runs.
29
-
30
- A rule that only routed a path to a standard now names the skill that owns
31
- the guidance. The tasks, plan, context, and skill rules are hand-written at
32
- `canon/` and `claude/skill.md`. A held copy of each syncs in place, and the
33
- groundwork, intake, session, design, readme, and rule copies are deleted as
34
- retired. The markdown rule is hand-written at `writing/markdown.md` and names
35
- the `markdown-craft` skill, and `800-prose` retires.
36
-
37
- A project's own rules belong under `.claude/rules/project/`, where a slug
38
- the toolkit also ships collides with nothing, and a rule placed under `canon/`
39
- instead is lost on the next sync.
40
-
41
- A sync never deletes a rule that a newer canon release installed. The stamp
42
- records the newest release that synced governance, and an older binary holds
43
- every rule it finds no source for, naming both releases and asking for an
44
- upgrade, rather than deleting what it cannot recognize.
45
-
46
- A renamed rule loses its old file and gains nothing under the new name unless
47
- the target's stamp records the stack it installed, in which case the new name
48
- shows as `missing` and `canon gov install <stack>` adds it.
49
-
50
- Design's own orphans are the target's overrides and are meant to stay where
51
- they are, so `canon design sync` deletes nothing it finds no source for.
52
-
53
- `canon design install` copies one toolkit-owned file to
54
- `.claude/design/base.css` and creates no override. A project overrides a value
55
- by writing `.claude/design/project/` itself, which `canon design sync` never
56
- touches, because that subfolder is project-authored by location the way
57
- `.claude/rules/project/` is. An override named exactly like the shipped file is
58
- still the project's.
59
-
60
- A project's faces sit in that subfolder too, under `fonts/` with plain
61
- `@font-face` rules in `fonts.css`. `canon design fonts add <file> --family <name>`
62
- copies a `woff2`, `woff`, `ttf`, or `otf` face in and appends its rule
63
- (`--weight`, `--style`, `--json`), refusing bytes that disagree with the
64
- extension as `invalid-font`. `fonts list --json` emits one row per rule, with a
65
- `problem` where the file is missing or lies outside the subfolder.
66
-
67
- The override ships absent rather than empty. An empty file is one the project
68
- did not ask for and did not write, the reconciliation already handles a missing
69
- side, and an empty override invites a target to fill it before it has an
70
- opinion.
71
-
72
- Nothing arrives on a project that has not run `canon design install`. The
73
- domain is detected by that folder existing, so `canon sync` skips it entirely on
74
- a target that never installed it, and the unstamped line at the end of
75
- `canon sync --check` stays quiet about it for the same reason. Governance is
76
- named there when it is absent and design is not, because a managed project
77
- without governance has yet to install what every project carries, while one
78
- without design chose that.
79
-
80
- When the target's install recorded a stack, `canon gov sync` also reports a
81
- rule that stack lists and `.claude/rules/canon/` does not hold, as a `missing`
82
- entry carrying no change. This is what makes a target whose recorded sync point
83
- postdates a rule joining its stack still see that rule: the report reads the
84
- target's current entitlement against its current tree rather than diffing
85
- from an anchor a later sync could advance past the rule's own commit. A
86
- target whose install predates the recorded chain falls back to the same
87
- rule-folder-inference `newRules` uses in `canon sync --check`.
88
-
89
- ## Tooling diff and sync
90
-
91
- `canon tooling diff <stack> [target]` is the read-only comparison, and a session asking what differs reaches for it by default. It writes nothing and exits 1 when anything differs and 0 when nothing does, so it gates CI. `--json` adds the scan record on stdout with `ok: true`, and the frame goes to stderr. A refusal such as an unknown stack, an excluded stack, or a toolkit-root target still exits 1 and answers `{ ok: false, reason, message }`, so a caller branches on the record and reads a typo'd stack name as a refusal rather than as drift.
92
-
93
- The record carries a `withheld` array beside `configs`, each entry `{ rel, stack, present }`. It fills only when the target sits below its git root: every `.github/` config or seed moves there rather than into `configs` or `seeds`, and none of them counts toward the exit, since GitHub never reads a workflow outside the root. `present` marks a copy an earlier sync already left in the subfolder, which the sync reports and never deletes. At a root, and for a target outside any repository, `withheld` is empty.
94
-
95
- `canon tooling sync <stack> [target] --check` reports the same list and always exits 0. It stays for scripts that already call it. `sync` itself writes only under `--write`, and a headless run with neither flag reports and exits 1.
96
-
97
- ## Install guards
98
-
99
- `canon gov install` requires its first argument under
100
- `CANON_NON_INTERACTIVE=1`. It used to fall back to an interactive picker that
101
- resolved to its first option headlessly, so a call with no stack installed
102
- whichever stack sorted first. It now reports the valid names on stderr and
103
- exits 1.
104
-
105
- Every documented agent path already passes the argument, including
106
- `canon init`. The confirm-then-apply prompt after it still resolves to `Yes`
107
- headlessly, so a call that names its stack is unchanged.
108
-
109
- `--add` with no stack installs the named rules into the one positional, or the
110
- current directory, and keeps the recorded chain. Pass `./node` to force a target
111
- named like a stack. [Rule detection](gov-detect.md) proposes the names.
112
-
113
- `canon gov install` also refuses the toolkit root as a target. It resolves the
114
- target before anything else, so a path that does not exist fails rather than
115
- being scaffolded.
116
-
117
- A target still on the flat installed-rule layout from an older release reports
118
- "No governance surfaces found in target" from `canon gov sync` and exits 0,
119
- since sync reads only the current wrapped destination. `canon gov install`
120
- against that same target writes a second, wrapped copy beside the flat one
121
- rather than reading or clearing it, so the target ends up holding both, and
122
- Claude Code loads both copies of an edited rule at session start. Run `canon
123
- migrate rule-layout` first to move a target off the flat layout, which reports
124
- what it would move and applies it under `--write`.
125
-
126
- ## Governance regen
127
-
128
- `canon gov regen` is the one governance verb that runs against the toolkit root,
129
- because the `.claude/rules/` it writes there is produced output rather than an
130
- operator's working copy. It reads the stack recorded in `internal/governance.toml`
131
- into `.claude/rules/canon/`, installs anything under `internal/rules/` into a
132
- separate `.claude/rules/internal/`, and clears both destinations first so a
133
- rule the record stopped naming disappears. `internal/` is what this repository
134
- alone carries: no `canon gov install` or `canon gov sync` target ever writes it,
135
- since a target has no rule source of its own that ships nowhere.
136
-
137
- It takes `--root <path>` and defaults to the toolkit root, prints nothing on success, and
138
- reports the reason on stderr with exit 1 when the record names a stack or rule
139
- that does not resolve. The Consumed copies stage of `bun run check` calls it and
140
- asserts both the generated rule folders and the consumed copy are committed.
8
+ What each verb writes, refuses, and leaves alone. `canon --help` lists flags.
9
+
10
+ ## Bootstrap
11
+
12
+ `canon init [path]` installs up to five core domains and reports each independently. A failed domain does not abort the run, which finishes the rest and exits 1 naming the failures.
13
+
14
+ Any flag skips the confirmation prompt, but the `base` default of `--stack` does not count, so a bare `canon init` still prompts. `--skip` takes `wiki`, `governance`, and `records`, and warns on any other value.
15
+
16
+ ## Governance install
17
+
18
+ - `canon gov install <stack> [target]` needs the stack under `CANON_NON_INTERACTIVE=1`, else lists the valid names on stderr and exits 1
19
+ - `--yes` answers the install confirm with no TTY and no env var. Without it, a headless call refuses and the message names `--yes`. It does not pick a stack.
20
+ - It refuses the toolkit root and a missing path
21
+ - `--add <rules>` with no stack installs into the one positional, or the current directory, keeping the recorded chain. Pass `./node` for a target named like a stack.
22
+
23
+ A flat installed-rule layout makes `gov sync` report "No governance surfaces found in target" and exit 0, and `gov install` write a second, wrapped copy. Run `canon migrate rule-layout` first.
24
+
25
+ ## Governance sync
26
+
27
+ `canon gov sync [target]` updates what `.claude/rules/canon/` holds and adds nothing. `.claude/rules/project/` is never read, so put project rules there.
28
+
29
+ - A rule the toolkit no longer ships is deleted, edited or not
30
+ - A rule renamed in `governance/renames.toml` moves, and its local edits are dropped
31
+ - A rule the recorded stack lists and the tree lacks reports as `missing` with no change
32
+ - A retired `.claude/GOV.md` is removed
33
+ - A binary older than the stamp holds every rule it has no source for and asks for an upgrade
34
+
35
+ After a rename the new name shows as `missing` when the stamp records a stack, and `gov install <stack>` adds it.
36
+
37
+ `canon gov regen [--root <path>]` runs against the toolkit root alone. It rewrites `.claude/rules/canon/` and `.claude/rules/internal/` from `internal/`, and exits 1 when the record names something that does not resolve.
38
+
39
+ ## Rule detection
40
+
41
+ `canon gov detect [target] [--stack <stack>] [--json]` proposes opt-in rules and writes nothing, from `governance/detect.toml` evidence keyed by rule basename.
42
+
43
+ - `deps` match package names in `package.json`, `pyproject.toml`, `go.mod`, and `composer.json`
44
+ - `files` are globs matched against git-listed paths and their folders, to depth two. Install what the operator keeps with `canon gov install --add <rules> <target>`.
45
+
46
+ ```json
47
+ {
48
+ "target": "",
49
+ "stack": "node",
50
+ "proposals": [
51
+ {
52
+ "rule": "react",
53
+ "evidence": [{ "kind": "dep", "match": "react", "path": "package.json" }]
54
+ }
55
+ ]
56
+ }
57
+ ```
58
+
59
+ - `kind` is `dep` or `file`, and `stack` is null without `--stack`, which drops rules that stack resolves
60
+ - A glob without a wildcard is also tested on disk, an unparseable manifest contributes nothing, and outside git the walk skips `.git`, `node_modules`, `vendor`, and `.venv`
61
+ - It exits 0 on any readable target and 1 for a missing target or an unresolved stack
62
+
63
+ ## Tooling
64
+
65
+ - `canon tooling diff <stack> [target]` is the read-only comparison, exiting 1 on any difference
66
+ - A `--json` refusal answers `{ ok: false, reason, message }`, so branch on the record
67
+ - `withheld` entries are `{ rel, stack, present }`, hold every `.github/` file below the git root, and count toward no exit
68
+ - `present` marks a copy an earlier sync left, which is reported and never deleted
69
+ - `canon tooling sync <stack> [target]` writes only under `--write`. `--check` reports and exits 0, and a headless run with neither flag exits 1.
70
+
71
+ `canon tooling inject` and `prune-gitignore` apply one stack with no scan or prompt, and the latter prints the number removed on stdout. Use `sync` unless scripting provisioning, and pass `--nested` inside an open frame.
72
+
73
+ ## Design
74
+
75
+ - `canon design install` copies `base.css` to `.claude/design/` and creates no override
76
+ - Project overrides live in `.claude/design/project/`, which `design sync` never touches
77
+ - `canon design fonts add <file> --family <name>` copies a `woff2`, `woff`, `ttf`, or `otf` face into `project/fonts/` with its `fonts.css` rule, and refuses mismatched bytes as `invalid-font`
78
+ - `fonts list --json` flags a missing or out-of-folder file as a `problem`
79
+ - `canon sync` skips the domain where `design install` never ran
141
80
 
142
81
  ## Whole-project sync
143
82
 
144
- `canon sync` runs every installed domain sync, then offers to commit the result
145
- and open a pull request. Under `CANON_NON_INTERACTIVE=1` it applies the domain
146
- syncs and then refuses the git workflow, reporting the branch and commit it
147
- would have created and exiting 0. Nothing is staged, committed, or pushed
148
- headlessly.
149
-
150
- Run it interactively to reach the commit and pull request options.
151
- It also refuses a target whose working tree is dirty, so commit or stash first.
152
-
153
- ## Drift reporting
154
-
155
- `canon sync --check` reports drift and writes nothing, so it needs no clean tree
156
- and is safe to run at any time. Each file is classified as `stale` when it still
157
- matches what the toolkit installed, `customized` when the project edited it,
158
- `stranded` when it sits at a path the toolkit no longer installs to, `orphaned`
159
- when the project authored it, or `drifted` when no stamp covers it. Governance
160
- also reports `missing`, for a rule the target's recorded stack lists that its
161
- tree does not hold at all, `retired`, for a rule under
162
- `.claude/rules/canon/` the toolkit no longer ships, which the next sync
163
- deletes, and `renamed`, for one the toolkit ships under a new name, which the
164
- next sync moves. A `retired` or `renamed` file counts toward `--exit-code`,
165
- since one sync clears it.
166
-
167
- Use `--json` for the machine-readable report and `--exit-code` to fail a CI job. Orphaned
168
- and missing files are both excluded from that exit code: a project-authored
169
- rule never converges, and a sync that added a missing one silently changes
170
- what the project is governed by, which stays a separate command an operator
171
- chooses to run. Attribution reads `canon/config/config.json` first, which every
172
- install and sync now writes, and falls back to `.claude/canon/config.json` for
173
- a target that has not moved.
174
-
175
- A target installed before stamping shipped has no such file, and neither does
176
- that fallback do anything to migrate it. A target stamped before the file
177
- moved into `.claude/canon/` still carries it at the retired
178
- `.claude/canon.json`, and `canon sync --check` reads that path when the current
179
- one is absent, reporting it rather than moving it. Only a target carrying
180
- neither path falls back to the toolkit's own git history.
181
- Installed content matching any
182
- version that history ever published proves the file is untouched, so it reports
183
- `stale` naming the commit it came from, and content matching no published
184
- version stays `drifted`. A toolkit reached outside a git clone, which is what a
185
- registry install is, cannot run that fallback and reports
186
- `historyUnavailable` alongside the unattributed files.
187
-
188
- Each domain carries its own toolkit anchor in that file, so syncing one domain
189
- never advances the revision another measures from, and each reports the upstream
190
- commits touching its own source path. The `covers` field names the domains a
191
- target has actually stamped, so a domain that was never stamped is legible
192
- rather than reading as a clean one.
193
-
194
- ### Tooling
195
-
196
- Tooling records the stack chain an install resolved rather than per-file hashes,
197
- since `src/tooling/` runs its own inject machinery and has no walk to attribute.
198
- The chain is ordered nearest stack first, which is what a `--skip` run needs:
199
- recording the leaf alone would send the next report measuring against a layer the
200
- target deliberately does not carry. The report loads exactly those stacks, scans
201
- them the way `canon tooling diff` would, and counts what differs per category
202
- under `tooling.counts`.
203
-
204
- `measured` is the field the section exists for. A target carrying no chain
205
- reports `measured: false`, which separates tooling nobody has ever looked at from
206
- tooling that is current. Both produce zero changes otherwise. Every target
207
- installed before the record shipped starts unmeasured and leaves on its next
208
- `canon tooling sync`, since backfilling would mean inferring the chain from
209
- installed files, which is the guess the record replaces.
210
-
211
- A workspace root records nothing, because each package resolves its own chain and
212
- one written at the root would be that same guess. Run the check against a package
213
- to measure it. Tooling never counts toward `--exit-code`, on the grounds seeds
214
- are already excluded on, since it reports golden configs a project is expected to
215
- edit and a job counting those stays red with no remedy.
216
-
217
- ### Surfaces reported beside the domains
218
-
219
- Seven sections sit outside the per-domain scan, because each names something
220
- that walk cannot see: `seeds`, `superseded`, `unmigrated`, `unmovedSurfaces`,
221
- `newSkills`, `newRules`, and `reverse`. None of them produces a change, and no sync command
222
- applies any of them. What each reports, the managed-target gate they share, and
223
- which count toward `--exit-code` are in `drift-surfaces.md`.
83
+ `canon sync [path]` runs every installed domain sync, then offers a commit and pull request. Headlessly it applies the syncs, refuses the git workflow, reports what it would have made, and exits 0. It refuses a dirty working tree.
224
84
 
225
- ## Bootstrap
85
+ ## Drift report
86
+
87
+ `canon sync --check` writes nothing and needs no clean tree. `--json` emits the record, `--exit-code` fails CI.
88
+
89
+ | State | Meaning |
90
+ | ------------ | ------------------------------------------------------------------ |
91
+ | `stale` | Still matches what the toolkit installed |
92
+ | `customized` | The project edited it |
93
+ | `stranded` | Sits at a path the toolkit no longer installs to |
94
+ | `orphaned` | The project authored it |
95
+ | `drifted` | No stamp covers it |
96
+ | `missing` | Governance only. The recorded stack lists it and the tree lacks it |
97
+ | `retired` | Governance only. The toolkit no longer ships it |
98
+ | `renamed` | Governance only. The toolkit ships it under a new name |
99
+
100
+ `retired` and `renamed` count toward `--exit-code`. `orphaned` and `missing` do not.
101
+
102
+ - Attribution reads `canon/config/config.json`, then `.claude/canon/config.json`, then the retired `.claude/canon.json`, and moves none
103
+ - With none it matches the toolkit's git history, where a published version is `stale` and anything else `drifted`. A registry install has none and reports `historyUnavailable`.
104
+ - `covers` names the domains the target stamped, and each domain keeps its own anchor
105
+ - Tooling records its stack chain, nearest first, and never counts toward `--exit-code`
106
+ - `tooling.measured: false` means none was recorded. It clears on the next `canon tooling sync`. A workspace root records nothing, so run the check against a package.
107
+
108
+ ## Side sections
109
+
110
+ Seven sections sit outside the per-domain scan, and no sync applies them. Each returns empty when `managed` is false. A managed target holds `.claude/`, a `CLAUDE.md`, or a root-layout domain, and an unmanaged one is routed to `canon init`.
111
+
112
+ | Section | Reports | Exit |
113
+ | ----------------- | --------------------------------------------------------------------------------------- | ---- |
114
+ | `seeds` | Each seed as `matching`, `stale`, `drifted`, or `missing`. Merge an edited one by hand. | No |
115
+ | `superseded` | A file a newer seed folder replaced, as `replacedBy`. Never deleted. | No |
116
+ | `unmigrated` | A domain at its old root, with `rootPath`, `installPath`, and a count | Yes |
117
+ | `unmovedSurfaces` | A surface at its `.claude/` spelling, as `rel` and `movesTo` | No |
118
+ | `newRules` | A rule the recorded stack lists and the tree lacks | No |
119
+ | `newSkills` | A plugin skill the target has not seen. Needs no command. | No |
120
+ | `reverse` | `unclaimed` folders the toolkit once shipped | No |
121
+
122
+ The Exit column marks what counts toward `--exit-code`. `canon migrate surface-roots` closes `unmovedSurfaces`, and `canon gov install` closes `newRules`. A `reverse` entry is attributed `dropped`, `project`, or `unattributed`.
123
+
124
+ `newRules` and `newSkills` come back empty on a shallow clone or registry install, which is unmeasured rather than clean.
125
+
126
+ ## Version skew
127
+
128
+ `canon sync --check` and `canon claude skills drift` report the installed version against the newest published one in `skew.state`. The report never changes an exit code, so branch on the field.
129
+
130
+ | State | Meaning |
131
+ | --------- | ---------------------------------------------------- |
132
+ | `behind` | Names `canon upgrade` as the remedy |
133
+ | `current` | Installed matches published |
134
+ | `unknown` | Carries the reason the registry could not be reached |
226
135
 
227
- `canon init` installs up to five core domains and reports each one independently. A
228
- domain that fails does not abort the run, so the command finishes the rest and
229
- exits 1 naming the failures. Passing any flag skips the confirmation prompt,
230
- which is what makes it scriptable.
231
-
232
- `--stack` defaults to `base`, and the default
233
- does not read as a passed flag, so a bare `canon init` installs governance and
234
- still prompts. `--skip` takes `wiki`, `governance`, and `records`, and warns
235
- without aborting on any other value. Records has nothing to run non-interactively,
236
- since the private backup repository does not exist yet at scaffold time, so its
237
- step only prints the one-time setup reminder and `--skip records` silences it.
238
-
239
- ## Unguarded tooling primitives
240
-
241
- `canon tooling inject` and `canon tooling prune-gitignore` are the unguarded
242
- primitives beneath `sync`. They apply one stack with no scan and no prompt, and
243
- they deliberately skip the check that rejects `claude`, which is how `canon
244
- claude` drives its own stack through them. Use `sync` unless you are scripting
245
- provisioning. Both frame their own output, so pass `--nested` when calling from
246
- inside an already-open frame.
136
+ `canon upgrade --json` adds `pending`, where the checkout holds a release npm does not serve yet. `checkout` names it, `message` starts `CLI stays at`, and the exit is 0.
@@ -1,126 +1,84 @@
1
1
  ---
2
2
  title: Markdown audit
3
- description: Running the audit over any markdown path, where its bans and checkpoints are read from, what each check reports, and why the ban half gates while the structural half reports
3
+ description: Running the markdown audit over any path, each finding it reports with whether it gates, the six density limits, the advisory measures, the exit codes, and where the audit runs
4
+ kind: reference
4
5
  ---
5
6
 
6
7
  # Markdown audit
7
8
 
8
- `canon markdown audit [path...]` reports any markdown file against the `markdown-craft` skill's `markdown.md` reference. That reference governs a file rather than a folder, so this resolves no folder and requires no `index.md`, which is what puts `.claude/rules/` and `governance/` in reach. Folder-shaped findings stay in `canon context audit`, described in `context-audit.md`.
9
+ `canon markdown audit [path...]` reports any markdown file against the `markdown-craft` skill's `markdown.md`. It resolves no folder and needs no `index.md`, so `.claude/rules/` and `governance/` are in reach. Folder-shaped findings belong to `canon context audit`, in `context-audit.md`.
9
10
 
10
11
  ```bash
11
12
  canon markdown audit
12
13
  canon markdown audit --json
13
14
  canon markdown audit .claude/rules governance
14
- canon markdown audit docs/agents/commands.md
15
+ canon markdown audit docs/agents/install-and-sync.md
15
16
  canon markdown audit 'docs/**/*.md'
16
17
  ```
17
18
 
18
- | Option | Behavior |
19
- | -------- | ---------------------------------------------------------- |
20
- | `--json` | Add a machine-readable record on stdout, keeping the frame |
19
+ An argument is a file, a directory, or a glob, taken against the corpus git lists. A bare run measures every tracked or untracked-and-not-ignored file. An explicit file is taken as given, so a gitignored draft can be measured, and an argument matching nothing is named on the scope line.
21
20
 
22
- ## Scope
21
+ ## Output
23
22
 
24
- An argument is a file, a directory, or a glob. A directory narrows to everything under it and a glob narrows by match, both against the corpus git lists, which is what keeps `node_modules/` and the gitignored session-scratch folders out without naming either. An explicit file path is taken as given, so a gitignored draft can be measured before it is committed. Quote a glob the shell would expand first.
23
+ The ban set and checkpoints ship in `src/markdown/bans.ts` and `src/markdown/structure.ts`. `markdown.md` states each rule and the `write-human` skill states the cadence numbers, so move a number in both places in one change.
25
24
 
26
- A bare run measures every markdown file git lists, tracked plus untracked-and-not-ignored, so a file added on this branch is in scope on the branch that adds it. An argument matching no markdown file is named on the scope line rather than passed over, since a run measuring the paths that did resolve otherwise reads as a pass over one it never opened.
25
+ | Finding | Reports | Gates |
26
+ | --------------- | ------------------------------------------------------------------------------------ | ------------------------- |
27
+ | Bans | The two characters `markdown.md` bans, outside frontmatter, fences, spans, and links | Always |
28
+ | Links | A relative link resolving to nothing on disk | Always |
29
+ | Density | The six limits below, under `density` | On a `GATED_PATHS` prefix |
30
+ | Reference shape | A `kind: reference` page breaking its shape, under `referenceShape` | On a `GATED_PATHS` prefix |
31
+ | Colons | A colon followed by more text in a prose line, under `colons` | Never |
32
+ | Heading case | A capital after the first word of a heading or `title`, under `headingCase` | Never |
27
33
 
28
- ## Where the rules come from
34
+ A page marked `kind: reference` holds its opening, then `Output`, `Refusals`, and `See also` as H2s, each at most once and in that order. It takes no H3 and no table headed `Flag` or `Option`. An unmarked page is never measured.
29
35
 
30
- The ban set and all ten checkpoints ship with the `canon` package as data, in `src/markdown/bans.ts` and `src/markdown/structure.ts`. Every project is measured against the same set whether or not it installed the plugin, and no file has to resolve for a run to mean something.
36
+ | Density limit | Finding |
37
+ | ------------------- | ---------------------------------------------------------- |
38
+ | Paragraph length | A prose paragraph past 350 characters |
39
+ | Paragraphs in a row | More than 2 paragraphs with nothing else between them |
40
+ | Bullet length | A top-level bullet past 200 characters |
41
+ | Bullets in a row | More than 5 plain top-level bullets |
42
+ | Heading depth | Any heading past H3 |
43
+ | H3 opening | An H3 whose first block is neither a paragraph nor a table |
31
44
 
32
- Seven of the ten are stated in `markdown.md` and the three cadence numbers are stated in the `write-human` skill. That split is the content boundary rather than an accident: `markdown.md` carries the enforced rules a scan can decide, and the skill carries the rhythm rules a ban list cannot express. A cadence number moved in the skill and left in the code drifts the same way, so move both.
45
+ - A paragraph under a list item counts toward that bullet, never as a paragraph
46
+ - A table, fence, list, heading, or bold section marker ends a run of paragraphs
47
+ - A run whose every item opens with a bold label or a link is exempt, and numbered items never count
48
+ - Only context entries, docs, skill bodies and references, tooling seeds and stack references, the wiki, `README.md`, and `CONTRIBUTING.md` take a finding
49
+ - A bullet run gates everywhere those paths sit, and the rest gates under `GATED_PATHS` in `src/markdown/scope.ts`. Outside a canon checkout nothing gates.
33
50
 
34
- Reading them out of the reference per run was the original design. It put a parser contract on a document authored for people, and the reference had to carry a paragraph of its own warning an author that a one-word backticked example in a `- Do not use ` bullet would be lifted into a literal ban set and ban that word everywhere. A rule existing to protect a parser from the prose it parses is the argument for separating them.
51
+ Five measures report and never set the exit code on their own.
35
52
 
36
- `markdown.md` still states every ban and every checkpoint, and a reader follows it rather than the code. Nothing compares the two, so a number moved in one place and left in the other drifts silently. Move both in the same change.
53
+ | Measure | Reports | Gate or report |
54
+ | ---------- | ------------------------------------------------------------------------------------ | -------------------------------------- |
55
+ | Bullets | A bullet past 200 characters | Gates through the density length |
56
+ | Paragraphs | A paragraph past 4 sentences or 350 characters | Gates through the density length |
57
+ | Cadence | 3 or more sentences with a spread of 5 words or under, or a word opening more than 2 | Reports |
58
+ | Depth | A run past about 40 rendered lines at 80 columns | Reports |
59
+ | Length | A document past 300 rendered lines, unless it carries `canon-length-exempt` | Reports, and the gate owns the verdict |
37
60
 
38
- The set is closed rather than extensible, so a project cannot add a term by editing a file. It holds the two characters alone, since a character is the one class a literal match settles. The set once carried 13 words and 6 British spellings as well. The words moved to the `write-human` skill as guidance, because a word ban catches the token and misses the habit, and `just`, `allows`, and `very` carry honest uses no literal match separates. Spelling is left to the author, with American spelling carried as guidance in `markdown-craft`.
61
+ | Code | Meaning |
62
+ | ---- | ------------------------------------------------------------------ |
63
+ | `0` | No gating finding |
64
+ | `1` | Refused, with the reason on stderr |
65
+ | `2` | A ban, a dead link, or a gating density or reference shape finding |
66
+ | `3` | The shipped ban set is empty, so nothing was measured |
39
67
 
40
- A set shipped empty is reported rather than passed. It finds nothing and would exit clean, which reports a corpus nobody checked as a corpus carrying no violation, so the run names the empty set and exits `3`. The set ships with the package, so a defect in the build is the only cause left.
68
+ Rewrite the sentence on a ban hit rather than swapping the token. A code span clears the report only where the token is an identifier under discussion.
41
69
 
42
- ## What each check reports
70
+ The audit runs in four places:
43
71
 
44
- ### Bans
72
+ - `.claude/hooks/standards-audit.sh` after each markdown edit, handing back every finding
73
+ - The `Markdown` stage of `canon gate run`, over the whole corpus
74
+ - `scripts/verify.ts` in a scaffolded project, excluding `CHANGELOG.md` and skipping with a logged line when no `canon` is on `PATH`
75
+ - The seeded hook in a target
45
76
 
46
- One closed set reports a hit: the characters `markdown.md` bans under `## Punctuation`.
77
+ ## Refusals
47
78
 
48
- Frontmatter, fenced blocks, inline code spans, and link destinations are excluded. Without the code-span exclusion each reference would report its own backticked examples, and without the link exclusion a semicolon in a query string would report as prose no rewrite can fix.
79
+ - `no-git` or `no-markdown`: no corpus can be built, and the gate reads it as unmeasured
49
80
 
50
- Word choice stays unmeasured and the report says so on every run. Single words travel in the `write-human` skill as guidance a reader applies. A multi-word ban escapes the harvest by width, whether it carries a placeholder standing in for the rest of the sentence or spells the phrase out in full, and every rule under `## Voice` is a judgment. The bans `## Language` states over what a sentence may claim are patterns of the multi-word kind by construction, since a literal match over a pattern reports the compliant text and reaches none of the violations. A report listing hits without naming those would read as a verdict on the whole reference.
81
+ ## See also
51
82
 
52
- ### Links
53
-
54
- A relative link's destination resolves against the filesystem, over the same corpus the ban scan reads. A destination is skipped rather than resolved when it is empty, opens with a URL scheme, opens with `#` as a same-file anchor, opens with `/` as a root-absolute reference, or carries `<` as a template placeholder such as `<slug>` or `<name>`. What remains is split on its first `#`, decoded, and resolved against the linking file's own folder, and a destination resolving to nothing on disk reports.
55
-
56
- The placeholder exemption is the one live case the corpus needs. Every current instance is a genuine illustration rather than a literal path, and a marker-based override is available for the day a real one needs an exception instead.
57
-
58
- ### Colons and heading case
59
-
60
- Two advisory measures report beside the bans and never fail a run.
61
-
62
- The colon measure reports a colon followed by more text in a prose line, outside the label position. A list item's first colon, a bold or code span label, a line-final colon, a time, a URL, and anything inside a code span or a link are left alone. A heading, a table row, and a fenced line are skipped. Each finding carries the line, the column, the line's text, and the word before the colon as `lead` under `colons` in the `--json` entry.
63
-
64
- The heading-case measure reports a heading or a frontmatter `title` with a capitalized word after its first, outside code spans and all-caps tokens, under `headingCase`. A proper noun reports as well and is left as it is.
65
-
66
- `CHANGELOG.md` is exempt from both, since the release tool writes its headings.
67
-
68
- ### Weight, cadence, and length
69
-
70
- Bullet and paragraph weight, bullet stacks, run depth, the two cadence numbers, and the document length ceiling are each a judgment a reader settles rather than a fact a scan settles. What each measures, the samples behind its checkpoint, and why none of them gates are in `markdown-audit-weight.md`.
71
-
72
- ## Exit codes
73
-
74
- Exit codes are `0` for a completed run with no gating finding, `1` for a refusal, `2` for a ban hit or a dead relative link, and `3` for a shipped ban set that arrived empty. A banned character and a relative link resolving to nothing on disk each fail the run, both facts a scan settles rather than a reader judging. Bullet, paragraph, and depth weight are judgments a reader settles, and cadence is a distribution whose healthy range moves with the surface, so all four report under every code. A bullet stack, a run of more than 5 plain bullets, reports the same way, and the `bullet-stacks` stage in `canon gate run` owns its verdict. Length reports the same way, since the audit also runs on a plan kept out of version control, and the Document ceiling stage in `canon gate run` owns that verdict instead.
75
-
76
- `3` is separate from `1` because the two want different responses from a caller. A refusal means no corpus was built, and the `Markdown bans` stage in `canon gate run` is right to report it as unmeasured rather than as a pass. An empty set means the corpus was walked and nothing was looked for, so that stage fails the push on `3` rather than skipping.
77
-
78
- `2` rather than `1` for the gate keeps a measurement that succeeded and found something distinct from the audit declining to measure at all. A caller reading one as the other sends a reader hunting a defect that does not exist, which is the distinction `canon context audit` and the gate's own seed stage already draw between the same two codes.
79
-
80
- A banned character is a fact rather than a judgment, which is the test that admits it to a gate. What held it back was that gating on day one against a corpus never checked mechanically fails loudly on work nobody has had a chance to fix. The order was to land the verb reporting, measure the corpus once, fix what it finds, and turn the gate on as its own change, and the gate is the last of the four.
81
-
82
- Of the five measures a first full reading took, the ban count is the only one a gate should ever read.
83
-
84
- The ban half reached zero, which was the precondition the gate waited on, and it was re-measured against the same corpus at the moment the gate landed. Three sweep slices then took the structural half, and the corpus reports 1 heavy bullet and 22 heavy paragraphs across 6 files on 2026-08-06, every one on a ground the triage record holds as exempt.
85
-
86
- Depth is the one measure no slice triaged, so its figure moved with the sweep rather than with the corpus. The break rule widened to bold section markers on 2026-08-28 and again to the colon-less ones the same day, and each drop came with no edit to any file, so the 41 above reads against a rule the command no longer runs. Read all four structural counts from a run rather than from this paragraph.
87
-
88
- ### What a hit asks of an author
89
-
90
- Rewrite the sentence rather than swapping the banned token for a near-synonym. The rule is about the sense the token carries, so a swap that keeps the sense clears the report without clearing the violation.
91
-
92
- A code span clears the report too, since the ban scan walks around one, and it is the answer only where the token is genuinely an identifier under discussion. `## Code and identifiers` in `markdown.md` reserves the span for commands, API names, file paths, and identifiers, so backticking a quoted utterance spends one rule to satisfy another and leaves the corpus no cleaner.
93
-
94
- A hit the closed set could not separate from correct prose was the case with no third option while the set still carried words. The temporal `just` reported as the vague qualifier it happened to spell, and rewriting the sentence is what the toolkit settled on over building an exemption path, since an exemption landing in the verb and not the edit hook leaves the line failing where an author actually meets it. That class is part of why the words left the gate.
95
-
96
- ### Where the rules are enforced
97
-
98
- Four surfaces apply the ban set and all four go through this verb. `.claude/hooks/standards-audit.sh` runs it against a single file after each markdown edit, the seed copy a project installs does the same, the `Markdown bans` stage in `canon gate run` runs it across the whole corpus before this repository's own push, and the same-named stage in the one verify runner every stack shares, `scripts/verify.ts`, runs it across a scaffolded project's tracked markdown before its own push. Each hook parsed its own copy of the word bans in awk before that, which left a British spelling passing at edit time and failing the push with nothing in between explaining the difference.
99
-
100
- The seed copy moved onto the verb when the sets became data, since its awk had nothing left to parse. It resolves one runner where the toolkit copy resolves two, looking for no checkout source, and a machine carrying no `canon` gets a report naming the binary to install rather than a silent pass. The Seed independence stage of `bun run check` scopes its walk to markdown and leaves the seed hooks outside it, which its own comment records as deliberate.
101
-
102
- The target push stage reads the same exit codes `canon gate run`'s own stage reads: 0 passes, 1 is a refusal logged as a skip, 2 is a finding, and 3 is a shipped-empty ban set. It skips and logs rather than failing when no `canon` binary is on the machine's PATH, since a scaffolded project is not guaranteed the toolkit is installed, and its skip line names the install command.
103
-
104
- The target push stage excludes `CHANGELOG.md`, unlike every other surface here. A changelog a generator builds from commit subjects carries prose nobody wrote against the ban set, where this repository's own passes only because `release-please` builds it from subjects sessions already wrote to the rule. The stage passes an explicit file list rather than the bare invocation the other surfaces use, so a corpus with no file left once the changelog is set aside reads as a pass rather than falling back to the whole tree.
105
-
106
- The hook prefers a checkout's own `src/cli.ts` over a globally installed binary, so it and the push stage read one build. A published binary lags a branch by whatever has not been released, which would put a ban kind added on the branch into the push and not into the edit. It reads its findings out of the `--json` record rather than off the exit code, so an older binary still reports where the fallback applies. It reads `bans.emptySets` out of the same record, so a set the verb shipped empty reaches the author as a check narrowed to what it could measure rather than as a clean pass.
107
-
108
- It also relays the two advisory measures from the same record. A heading finding reports for the whole file, and a colon finding reports only where the word before the colon and the colon appear in the text the write carried, so an edit is never asked to answer for a colon it left untouched.
109
-
110
- That field replaced `bans.missingStandards`, which answered a standard resolving under none of three roots. The set ships with the package now, so the state it named cannot occur and the narrowed check has one cause left, a defect in the build. The hook keeps reading a field either way, since a reader cannot tell a narrowed check from a clean one without it.
111
-
112
- Both hooks answer an absent record as well. A completed run always writes the record and a refusal writes none, so an empty one means the verb declined to measure rather than measured and found nothing. The verb needs a git repository to build its corpus and refuses without one, which is a project the seeded hook can be installed into, and reading the findings alone reported that as a clean file.
113
-
114
- A machine with neither runner still blocks no edit, and it says so rather than exiting clean. The push stage holds either way. An edit nobody checked and an edit carrying no violation are one silence to a reader, so the enforcement a machine lacks is reported rather than inferred.
115
-
116
- The stage measures the whole corpus rather than the changed files. A `Do not use` bullet added to a reference bans a token retroactively, and no file in the push that adds the bullet was edited.
117
-
118
- ## What it does not cover
119
-
120
- The verb reads the `markdown-craft` skill's reference and the `write-human` cadence numbers and nothing else. The references the `git-pr`, `git-branch`, and `git-commit` skills own, `publish.md`, `slug.md`, and `versioning.md`, govern an attribute of other artifacts, and none of them is implemented here.
121
-
122
- `publish.md` describes a scan applying the same punctuation bans to finished text on its way out. No code implemented that scan before this command, so nothing is duplicated, and a later surface should call this verb rather than build a second one.
123
-
124
- That reference's cross-reference rule is out of reach rather than merely unimplemented. It resolves by destination, bare where the destination auto-links a pull request or issue number and backticked where it does not, so this verb sees only the half where the backticked form is correct. A check here would report every compliant reference in the tree and reach none of the published text that goes wrong, which is why the rule holds on reading alone.
125
-
126
- The list-density rule at `claude/skills/markdown-craft/references/markdown.md` is out of scope on purpose, since it carries no number and what a density figure should measure is still open.
83
+ - `context-audit.md`: the folder-shaped findings
84
+ - `audits.md`: the aggregate that runs this audit