@erclx/canon 4.0.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 (643) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/claude/.claude-plugin/plugin.json +19 -0
  4. package/claude/skills/bash-cli-script/REQUIREMENT.md +42 -0
  5. package/claude/skills/bash-cli-script/SKILL.md +48 -0
  6. package/claude/skills/bash-cli-script/references/template.md +43 -0
  7. package/claude/skills/bash-script/REQUIREMENT.md +36 -0
  8. package/claude/skills/bash-script/SKILL.md +100 -0
  9. package/claude/skills/bash-script/references/patterns.md +349 -0
  10. package/claude/skills/canon-cli/REQUIREMENT.md +41 -0
  11. package/claude/skills/canon-cli/SKILL.md +103 -0
  12. package/claude/skills/canon-feedback-file/REQUIREMENT.md +40 -0
  13. package/claude/skills/canon-feedback-file/SKILL.md +80 -0
  14. package/claude/skills/canon-feedback-triage/REQUIREMENT.md +40 -0
  15. package/claude/skills/canon-feedback-triage/SKILL.md +63 -0
  16. package/claude/skills/canon-operator/REQUIREMENT.md +61 -0
  17. package/claude/skills/canon-operator/SKILL.md +108 -0
  18. package/claude/skills/canon-rollout/REQUIREMENT.md +59 -0
  19. package/claude/skills/canon-rollout/SKILL.md +147 -0
  20. package/claude/skills/canon-screencast/REQUIREMENT.md +39 -0
  21. package/claude/skills/canon-screencast/SKILL.md +167 -0
  22. package/claude/skills/canon-slides-draft/REQUIREMENT.md +39 -0
  23. package/claude/skills/canon-slides-draft/SKILL.md +62 -0
  24. package/claude/skills/ci-workflow/REQUIREMENT.md +40 -0
  25. package/claude/skills/ci-workflow/SKILL.md +65 -0
  26. package/claude/skills/ci-workflow/references/workflows.md +98 -0
  27. package/claude/skills/claude-address-review/REQUIREMENT.md +57 -0
  28. package/claude/skills/claude-address-review/SKILL.md +212 -0
  29. package/claude/skills/claude-address-review/references/rebase-conflicts.md +39 -0
  30. package/claude/skills/claude-autoship/REQUIREMENT.md +50 -0
  31. package/claude/skills/claude-autoship/SKILL.md +207 -0
  32. package/claude/skills/claude-design-extract/REQUIREMENT.md +42 -0
  33. package/claude/skills/claude-design-extract/SKILL.md +102 -0
  34. package/claude/skills/claude-diagram/REQUIREMENT.md +45 -0
  35. package/claude/skills/claude-diagram/SKILL.md +177 -0
  36. package/claude/skills/claude-docs/REQUIREMENT.md +60 -0
  37. package/claude/skills/claude-docs/SKILL.md +287 -0
  38. package/claude/skills/claude-docs/references/anchor-sweep.md +58 -0
  39. package/claude/skills/claude-docs/references/wireframe-sweep.md +45 -0
  40. package/claude/skills/claude-feature/REQUIREMENT.md +36 -0
  41. package/claude/skills/claude-feature/SKILL.md +115 -0
  42. package/claude/skills/claude-groundwork/REQUIREMENT.md +48 -0
  43. package/claude/skills/claude-groundwork/SKILL.md +142 -0
  44. package/claude/skills/claude-intake/REQUIREMENT.md +49 -0
  45. package/claude/skills/claude-intake/SKILL.md +114 -0
  46. package/claude/skills/claude-intake-answer/REQUIREMENT.md +48 -0
  47. package/claude/skills/claude-intake-answer/SKILL.md +90 -0
  48. package/claude/skills/claude-markdown-propose/REQUIREMENT.md +48 -0
  49. package/claude/skills/claude-markdown-propose/SKILL.md +118 -0
  50. package/claude/skills/claude-markdown-propose/references/format.md +107 -0
  51. package/claude/skills/claude-memory-capture/REQUIREMENT.md +50 -0
  52. package/claude/skills/claude-memory-capture/SKILL.md +101 -0
  53. package/claude/skills/claude-memory-review/REQUIREMENT.md +50 -0
  54. package/claude/skills/claude-memory-review/SKILL.md +210 -0
  55. package/claude/skills/claude-memory-review/references/receipt-format.md +48 -0
  56. package/claude/skills/claude-orchestrate/REQUIREMENT.md +121 -0
  57. package/claude/skills/claude-orchestrate/SKILL.md +241 -0
  58. package/claude/skills/claude-orchestrate/references/orchestrator-dispatch.md +132 -0
  59. package/claude/skills/claude-orchestrate/references/orchestrator-handoff.md +32 -0
  60. package/claude/skills/claude-orchestrate/references/orchestrator-parked.md +72 -0
  61. package/claude/skills/claude-orchestrate/references/orchestrator-poll.md +87 -0
  62. package/claude/skills/claude-orchestrate/references/orchestrator-resume.md +30 -0
  63. package/claude/skills/claude-orchestrate/references/orchestrator-sweep.md +21 -0
  64. package/claude/skills/claude-orchestrate/scripts/poll.sh +373 -0
  65. package/claude/skills/claude-orchestrate/scripts/watch.sh +181 -0
  66. package/claude/skills/claude-pr-review/REQUIREMENT.md +47 -0
  67. package/claude/skills/claude-pr-review/SKILL.md +295 -0
  68. package/claude/skills/claude-review/REQUIREMENT.md +39 -0
  69. package/claude/skills/claude-review/SKILL.md +142 -0
  70. package/claude/skills/claude-seed-sync/REQUIREMENT.md +45 -0
  71. package/claude/skills/claude-seed-sync/SKILL.md +156 -0
  72. package/claude/skills/claude-standards-audit/REQUIREMENT.md +33 -0
  73. package/claude/skills/claude-standards-audit/SKILL.md +99 -0
  74. package/claude/skills/claude-tasks/REQUIREMENT.md +43 -0
  75. package/claude/skills/claude-tasks/SKILL.md +161 -0
  76. package/claude/skills/claude-teach/REQUIREMENT.md +56 -0
  77. package/claude/skills/claude-teach/SKILL.md +196 -0
  78. package/claude/skills/claude-teach/references/lesson-craft.md +59 -0
  79. package/claude/skills/claude-teach/references/pedagogy.md +67 -0
  80. package/claude/skills/claude-teach/references/promotion.md +54 -0
  81. package/claude/skills/claude-ui-test/REQUIREMENT.md +40 -0
  82. package/claude/skills/claude-ui-test/SKILL.md +77 -0
  83. package/claude/skills/claude-ux-audit/REQUIREMENT.md +40 -0
  84. package/claude/skills/claude-ux-audit/SKILL.md +79 -0
  85. package/claude/skills/claude-ux-measure/REQUIREMENT.md +48 -0
  86. package/claude/skills/claude-ux-measure/SKILL.md +122 -0
  87. package/claude/skills/claude-worker/REQUIREMENT.md +54 -0
  88. package/claude/skills/claude-worker/SKILL.md +96 -0
  89. package/claude/skills/claude-worktree/REQUIREMENT.md +58 -0
  90. package/claude/skills/claude-worktree/SKILL.md +136 -0
  91. package/claude/skills/create-rule/REQUIREMENT.md +45 -0
  92. package/claude/skills/create-rule/SKILL.md +68 -0
  93. package/claude/skills/create-skill/REQUIREMENT.md +38 -0
  94. package/claude/skills/create-skill/SKILL.md +32 -0
  95. package/claude/skills/create-snippet/REQUIREMENT.md +39 -0
  96. package/claude/skills/create-snippet/SKILL.md +30 -0
  97. package/claude/skills/create-standard/REQUIREMENT.md +35 -0
  98. package/claude/skills/create-standard/SKILL.md +29 -0
  99. package/claude/skills/decision-escalate/REQUIREMENT.md +45 -0
  100. package/claude/skills/decision-escalate/SKILL.md +79 -0
  101. package/claude/skills/docs-sync/REQUIREMENT.md +41 -0
  102. package/claude/skills/docs-sync/SKILL.md +95 -0
  103. package/claude/skills/git-branch/REQUIREMENT.md +38 -0
  104. package/claude/skills/git-branch/SKILL.md +60 -0
  105. package/claude/skills/git-commit/REQUIREMENT.md +36 -0
  106. package/claude/skills/git-commit/SKILL.md +49 -0
  107. package/claude/skills/git-followup/REQUIREMENT.md +43 -0
  108. package/claude/skills/git-followup/SKILL.md +48 -0
  109. package/claude/skills/git-issue/REQUIREMENT.md +38 -0
  110. package/claude/skills/git-issue/SKILL.md +65 -0
  111. package/claude/skills/git-pr/REQUIREMENT.md +50 -0
  112. package/claude/skills/git-pr/SKILL.md +164 -0
  113. package/claude/skills/git-pr/references/labels.md +95 -0
  114. package/claude/skills/git-ship/REQUIREMENT.md +42 -0
  115. package/claude/skills/git-ship/SKILL.md +55 -0
  116. package/claude/skills/git-split/REQUIREMENT.md +39 -0
  117. package/claude/skills/git-split/SKILL.md +162 -0
  118. package/claude/skills/git-stage/REQUIREMENT.md +39 -0
  119. package/claude/skills/git-stage/SKILL.md +73 -0
  120. package/claude/skills/git-worktree/REQUIREMENT.md +38 -0
  121. package/claude/skills/git-worktree/SKILL.md +130 -0
  122. package/claude/skills/migration-claude-md/REQUIREMENT.md +40 -0
  123. package/claude/skills/migration-claude-md/SKILL.md +76 -0
  124. package/claude/skills/migration-context/REQUIREMENT.md +36 -0
  125. package/claude/skills/migration-context/SKILL.md +95 -0
  126. package/claude/skills/migration-standards-drop/REQUIREMENT.md +55 -0
  127. package/claude/skills/migration-standards-drop/SKILL.md +113 -0
  128. package/claude/skills/migration-superseded/REQUIREMENT.md +44 -0
  129. package/claude/skills/migration-superseded/SKILL.md +115 -0
  130. package/claude/skills/project-commands/REQUIREMENT.md +42 -0
  131. package/claude/skills/project-commands/SKILL.md +85 -0
  132. package/claude/skills/restate-plainly/REQUIREMENT.md +41 -0
  133. package/claude/skills/restate-plainly/SKILL.md +39 -0
  134. package/claude/skills/session-map/REQUIREMENT.md +57 -0
  135. package/claude/skills/session-map/SKILL.md +70 -0
  136. package/claude/skills/session-resume/REQUIREMENT.md +49 -0
  137. package/claude/skills/session-resume/SKILL.md +51 -0
  138. package/claude/skills/setup-gov/REQUIREMENT.md +37 -0
  139. package/claude/skills/setup-gov/SKILL.md +77 -0
  140. package/claude/skills/setup-indexes/REQUIREMENT.md +45 -0
  141. package/claude/skills/setup-indexes/SKILL.md +153 -0
  142. package/claude/skills/setup-init/REQUIREMENT.md +45 -0
  143. package/claude/skills/setup-init/SKILL.md +127 -0
  144. package/claude/skills/setup-plugins/REQUIREMENT.md +42 -0
  145. package/claude/skills/setup-plugins/SKILL.md +81 -0
  146. package/claude/skills/setup-plugins/references/plugin-catalog.md +53 -0
  147. package/claude/skills/setup-verify/REQUIREMENT.md +39 -0
  148. package/claude/skills/setup-verify/SKILL.md +51 -0
  149. package/claude/skills/systematic-debugging/REQUIREMENT.md +41 -0
  150. package/claude/skills/systematic-debugging/SKILL.md +70 -0
  151. package/claude/skills/write-human/REQUIREMENT.md +46 -0
  152. package/claude/skills/write-human/SKILL.md +68 -0
  153. package/claude/skills/write-human/references/density.md +38 -0
  154. package/claude/skills/write-human/references/machine-tells.md +107 -0
  155. package/claude/skills/write-human/references/source-material.md +37 -0
  156. package/claude/skills/youtube-transcripts/REQUIREMENT.md +38 -0
  157. package/claude/skills/youtube-transcripts/SKILL.md +34 -0
  158. package/docs/agents/audits.md +98 -0
  159. package/docs/agents/capture.md +37 -0
  160. package/docs/agents/census.md +23 -0
  161. package/docs/agents/commands.md +146 -0
  162. package/docs/agents/comments.md +34 -0
  163. package/docs/agents/context-audit-checks.md +120 -0
  164. package/docs/agents/context-audit.md +83 -0
  165. package/docs/agents/counts.md +76 -0
  166. package/docs/agents/demo.md +86 -0
  167. package/docs/agents/docs.md +17 -0
  168. package/docs/agents/gate.md +84 -0
  169. package/docs/agents/index.md +47 -0
  170. package/docs/agents/indexes.md +35 -0
  171. package/docs/agents/install-and-sync.md +385 -0
  172. package/docs/agents/intake.md +81 -0
  173. package/docs/agents/key-changes.md +103 -0
  174. package/docs/agents/label-coverage.md +73 -0
  175. package/docs/agents/markdown-audit.md +197 -0
  176. package/docs/agents/output-shape.md +70 -0
  177. package/docs/agents/overview.md +26 -0
  178. package/docs/agents/records.md +170 -0
  179. package/docs/agents/restated.md +81 -0
  180. package/docs/agents/review-classification.md +77 -0
  181. package/docs/agents/routing.md +61 -0
  182. package/docs/agents/rule-citations.md +98 -0
  183. package/docs/agents/sandbox.md +71 -0
  184. package/docs/agents/scripting.md +149 -0
  185. package/docs/agents/sessions.md +120 -0
  186. package/docs/agents/skills-audit.md +94 -0
  187. package/docs/agents/skills-reach.md +64 -0
  188. package/docs/agents/standards-audit.md +38 -0
  189. package/docs/agents/state-scoped-risk.md +105 -0
  190. package/docs/agents/superseded.md +85 -0
  191. package/docs/agents/targets.md +83 -0
  192. package/docs/agents/tasks.md +200 -0
  193. package/docs/agents/teach.md +158 -0
  194. package/docs/agents/test-order.md +56 -0
  195. package/docs/agents/worktrees.md +62 -0
  196. package/docs/ai-workflow.md +317 -0
  197. package/docs/index.md +23 -0
  198. package/docs/operating-model.md +223 -0
  199. package/docs/target-projects.md +258 -0
  200. package/docs/visual-design-workflow.md +151 -0
  201. package/docs/zshrc-aliases.md +65 -0
  202. package/governance/rules/ci/700-ci-workflow.md +44 -0
  203. package/governance/rules/claude/500-prose.md +15 -0
  204. package/governance/rules/claude/501-markdown.md +14 -0
  205. package/governance/rules/claude/510-context.md +28 -0
  206. package/governance/rules/claude/511-indexes.md +14 -0
  207. package/governance/rules/claude/520-wireframes.md +12 -0
  208. package/governance/rules/claude/530-requirements.md +11 -0
  209. package/governance/rules/claude/540-architecture.md +11 -0
  210. package/governance/rules/claude/550-design.md +11 -0
  211. package/governance/rules/claude/555-tasks.md +12 -0
  212. package/governance/rules/claude/556-groundwork.md +11 -0
  213. package/governance/rules/claude/557-intake.md +11 -0
  214. package/governance/rules/claude/558-plan.md +22 -0
  215. package/governance/rules/claude/559-memory.md +11 -0
  216. package/governance/rules/claude/560-diagrams.md +18 -0
  217. package/governance/rules/claude/561-teach.md +13 -0
  218. package/governance/rules/claude/562-session.md +15 -0
  219. package/governance/rules/claude/570-skill.md +24 -0
  220. package/governance/rules/claude/575-hooks.md +17 -0
  221. package/governance/rules/claude/576-settings.md +14 -0
  222. package/governance/rules/claude/580-readme.md +11 -0
  223. package/governance/rules/claude/590-rule-authoring.md +12 -0
  224. package/governance/rules/claude/591-standard-authoring.md +12 -0
  225. package/governance/rules/claude/592-claude-md.md +19 -0
  226. package/governance/rules/core/000-constitution.md +30 -0
  227. package/governance/rules/core/005-behavior.md +27 -0
  228. package/governance/rules/core/010-testing.md +35 -0
  229. package/governance/rules/core/015-output.md +20 -0
  230. package/governance/rules/core/020-concurrency.md +22 -0
  231. package/governance/rules/core/025-indexes.md +9 -0
  232. package/governance/rules/core/030-error-handling.md +31 -0
  233. package/governance/rules/core/035-tasks.md +13 -0
  234. package/governance/rules/core/040-performance.md +20 -0
  235. package/governance/rules/core/045-memory.md +12 -0
  236. package/governance/rules/core/050-logging.md +20 -0
  237. package/governance/rules/core/055-scratch.md +9 -0
  238. package/governance/rules/core/060-naming.md +19 -0
  239. package/governance/rules/core/065-spelling.md +19 -0
  240. package/governance/rules/core/070-planning.md +18 -0
  241. package/governance/rules/core/075-dependencies.md +25 -0
  242. package/governance/rules/core/080-config-comments.md +22 -0
  243. package/governance/rules/core/085-worktrees.md +17 -0
  244. package/governance/rules/core/087-git.md +11 -0
  245. package/governance/rules/core/090-code-comments.md +39 -0
  246. package/governance/rules/framework/200-react.md +51 -0
  247. package/governance/rules/framework/210-astro.md +41 -0
  248. package/governance/rules/framework/220-fastapi.md +43 -0
  249. package/governance/rules/framework/230-nextjs.md +48 -0
  250. package/governance/rules/framework/250-tailwind.md +32 -0
  251. package/governance/rules/framework/260-shadcn.md +34 -0
  252. package/governance/rules/lang/100-typescript.md +40 -0
  253. package/governance/rules/lang/110-python.md +42 -0
  254. package/governance/rules/lang/120-bash.md +19 -0
  255. package/governance/rules/lib/300-testing-ts.md +39 -0
  256. package/governance/rules/lib/305-e2e-reliability.md +34 -0
  257. package/governance/rules/lib/306-test-scope.md +25 -0
  258. package/governance/rules/lib/310-zod.md +25 -0
  259. package/governance/rules/lib/320-tanstack-query.md +32 -0
  260. package/governance/rules/lib/330-testing-py.md +44 -0
  261. package/governance/rules/lib/340-pydantic.md +38 -0
  262. package/governance/rules/lib/350-security-web.md +32 -0
  263. package/governance/rules/lib/360-security-server.md +39 -0
  264. package/governance/rules/lib/370-database.md +35 -0
  265. package/governance/rules/snippets/505-at-references.md +9 -0
  266. package/governance/rules/ui/400-ui.md +36 -0
  267. package/governance/rules/ui/410-a11y.md +48 -0
  268. package/governance/rules/ui/420-forms.md +36 -0
  269. package/governance/rules/ui/430-ux-completeness.md +65 -0
  270. package/governance/rules/ui/440-surface-capture.md +34 -0
  271. package/governance/rules/ui/450-link-behavior.md +19 -0
  272. package/governance/stacks/astro.toml +2 -0
  273. package/governance/stacks/base.toml +8 -0
  274. package/governance/stacks/node-server.toml +2 -0
  275. package/governance/stacks/node.toml +2 -0
  276. package/governance/stacks/python-fastapi.toml +2 -0
  277. package/governance/stacks/python.toml +2 -0
  278. package/governance/stacks/react.toml +2 -0
  279. package/package.json +69 -0
  280. package/scripts/config.sh +11 -0
  281. package/scripts/core/bootstrap.sh +81 -0
  282. package/scripts/core/check-color-source.sh +41 -0
  283. package/scripts/core/check-ignore-parity.sh +162 -0
  284. package/scripts/core/check-plugin-boundary.sh +45 -0
  285. package/scripts/core/check-seed-independence.sh +59 -0
  286. package/scripts/core/check-skill-paths.sh +24 -0
  287. package/scripts/core/clean.sh +36 -0
  288. package/scripts/core/install-check.sh +101 -0
  289. package/scripts/core/list-seed-roots.sh +18 -0
  290. package/scripts/core/regen-claude-copies.sh +10 -0
  291. package/scripts/core/regen-hero.sh +217 -0
  292. package/scripts/core/regen-indexes.sh +10 -0
  293. package/scripts/core/regen-tooling-paths.sh +61 -0
  294. package/scripts/core/repair-bare-flag.sh +19 -0
  295. package/scripts/core/snapshot.sh +134 -0
  296. package/scripts/core/update.sh +35 -0
  297. package/scripts/docs/list.sh +165 -0
  298. package/scripts/lib/frontmatter.sh +30 -0
  299. package/scripts/lib/gov.sh +14 -0
  300. package/scripts/lib/sandbox-fixtures.sh +191 -0
  301. package/scripts/lib/sandbox-git.sh +125 -0
  302. package/scripts/lib/sandbox-path.sh +206 -0
  303. package/scripts/lib/tooling.sh +35 -0
  304. package/scripts/lib/ui.sh +266 -0
  305. package/scripts/lib/worktree.sh +20 -0
  306. package/scripts/manage-sandbox.sh +466 -0
  307. package/scripts/snippets/create.sh +156 -0
  308. package/scripts/standards/list.sh +115 -0
  309. package/scripts/tooling/create.sh +109 -0
  310. package/scripts/tooling/verify.sh +179 -0
  311. package/snippets/align.md +12 -0
  312. package/snippets/claude/decision-memo.md +39 -0
  313. package/snippets/claude/feature-recap.md +19 -0
  314. package/snippets/claude/figma-steps.md +24 -0
  315. package/snippets/compact-summary.md +5 -0
  316. package/snippets/decision-help.md +6 -0
  317. package/snippets/meta-prompt.md +14 -0
  318. package/snippets/research-prompt.md +7 -0
  319. package/snippets/session-notes.md +11 -0
  320. package/snippets/snippets.toml +5 -0
  321. package/snippets/step-by-step.md +10 -0
  322. package/snippets/web-research.md +21 -0
  323. package/src/audits/baseline.ts +201 -0
  324. package/src/audits/catalog.ts +876 -0
  325. package/src/audits/run.ts +204 -0
  326. package/src/autoship/classify.ts +75 -0
  327. package/src/autoship/paths.ts +51 -0
  328. package/src/binary.ts +16 -0
  329. package/src/browser/engine.ts +40 -0
  330. package/src/census/count.ts +113 -0
  331. package/src/claude/cases/all.ts +24 -0
  332. package/src/claude/cases/authoring.ts +53 -0
  333. package/src/claude/cases/claude-workflow.ts +158 -0
  334. package/src/claude/cases/git.ts +44 -0
  335. package/src/claude/cases/misc.ts +27 -0
  336. package/src/claude/cases/setup.ts +94 -0
  337. package/src/claude/gitignore.ts +51 -0
  338. package/src/claude/routing.ts +283 -0
  339. package/src/claude/seeds-list.ts +47 -0
  340. package/src/claude/seeds.ts +150 -0
  341. package/src/claude/settings.ts +151 -0
  342. package/src/claude/skills-audit.ts +228 -0
  343. package/src/claude/skills-drift.ts +156 -0
  344. package/src/claude/skills-list.ts +99 -0
  345. package/src/claude/skills-rank.ts +320 -0
  346. package/src/claude/skills-reach.ts +227 -0
  347. package/src/cli-run.ts +43 -0
  348. package/src/cli.ts +200 -0
  349. package/src/commands/audits.ts +350 -0
  350. package/src/commands/autoship.ts +129 -0
  351. package/src/commands/capture.ts +133 -0
  352. package/src/commands/census.ts +105 -0
  353. package/src/commands/claude.ts +1286 -0
  354. package/src/commands/comments.ts +240 -0
  355. package/src/commands/context.ts +857 -0
  356. package/src/commands/demo.ts +389 -0
  357. package/src/commands/deps.ts +173 -0
  358. package/src/commands/design.ts +36 -0
  359. package/src/commands/docs.ts +60 -0
  360. package/src/commands/feedback-format.ts +23 -0
  361. package/src/commands/feedback.ts +112 -0
  362. package/src/commands/gate.ts +189 -0
  363. package/src/commands/gov.ts +1265 -0
  364. package/src/commands/indexes.ts +184 -0
  365. package/src/commands/init.ts +113 -0
  366. package/src/commands/intake.ts +406 -0
  367. package/src/commands/inventory.ts +256 -0
  368. package/src/commands/labels.ts +361 -0
  369. package/src/commands/markdown.ts +544 -0
  370. package/src/commands/migrate.ts +175 -0
  371. package/src/commands/pass-through.ts +39 -0
  372. package/src/commands/pr.ts +411 -0
  373. package/src/commands/records.ts +728 -0
  374. package/src/commands/sandbox.ts +468 -0
  375. package/src/commands/secrets.ts +132 -0
  376. package/src/commands/serve.ts +159 -0
  377. package/src/commands/sessions.ts +408 -0
  378. package/src/commands/slides.ts +126 -0
  379. package/src/commands/snippets.ts +84 -0
  380. package/src/commands/standards.ts +247 -0
  381. package/src/commands/sync.ts +428 -0
  382. package/src/commands/targets.ts +319 -0
  383. package/src/commands/tasks.ts +743 -0
  384. package/src/commands/teach.ts +786 -0
  385. package/src/commands/tooling.ts +573 -0
  386. package/src/commands/transcripts.ts +44 -0
  387. package/src/commands/upgrade.ts +231 -0
  388. package/src/commands/wiki.ts +100 -0
  389. package/src/commands/worktrees.ts +191 -0
  390. package/src/comments/scan.ts +338 -0
  391. package/src/comments/trend.ts +207 -0
  392. package/src/comments/vocabulary.ts +85 -0
  393. package/src/context/architecture.ts +364 -0
  394. package/src/context/audit.ts +790 -0
  395. package/src/context/citations.ts +196 -0
  396. package/src/context/folders.ts +186 -0
  397. package/src/context/gate.ts +57 -0
  398. package/src/context/index-drift.ts +64 -0
  399. package/src/context/narration.ts +99 -0
  400. package/src/copy.ts +30 -0
  401. package/src/counts/catalogs.ts +96 -0
  402. package/src/counts/numbers.ts +79 -0
  403. package/src/counts/scan.ts +314 -0
  404. package/src/demo/beats.ts +135 -0
  405. package/src/demo/compile.ts +326 -0
  406. package/src/demo/container.ts +63 -0
  407. package/src/demo/cursors.ts +55 -0
  408. package/src/demo/drive.ts +357 -0
  409. package/src/demo/pointer.ts +178 -0
  410. package/src/demo/theme.ts +112 -0
  411. package/src/deps/audit.ts +153 -0
  412. package/src/design/parse.ts +116 -0
  413. package/src/design/render.ts +249 -0
  414. package/src/docs/read.ts +77 -0
  415. package/src/exec.ts +16 -0
  416. package/src/exempt-marker.ts +43 -0
  417. package/src/frontmatter.ts +13 -0
  418. package/src/gate/measures.ts +682 -0
  419. package/src/gate/sequencer.ts +386 -0
  420. package/src/gate/stages.ts +412 -0
  421. package/src/git-env.ts +36 -0
  422. package/src/git-files.ts +105 -0
  423. package/src/git-ignore.ts +46 -0
  424. package/src/github-format.ts +13 -0
  425. package/src/github.ts +24 -0
  426. package/src/gov/adapter.ts +103 -0
  427. package/src/gov/citations.ts +514 -0
  428. package/src/gov/consumed.ts +129 -0
  429. package/src/gov/install.ts +132 -0
  430. package/src/gov/list.ts +106 -0
  431. package/src/gov/payload.ts +39 -0
  432. package/src/gov/restated.ts +814 -0
  433. package/src/gov/stacks.ts +205 -0
  434. package/src/gov/superseded.ts +415 -0
  435. package/src/gov/test-order.ts +407 -0
  436. package/src/indexes/frontmatter.ts +46 -0
  437. package/src/indexes/regen.ts +84 -0
  438. package/src/indexes/render.ts +201 -0
  439. package/src/indexes/walk.ts +83 -0
  440. package/src/init/flags.ts +60 -0
  441. package/src/init/plan.ts +114 -0
  442. package/src/init/run.ts +46 -0
  443. package/src/init/steps.ts +77 -0
  444. package/src/intake/folder.ts +320 -0
  445. package/src/intake/items.ts +174 -0
  446. package/src/inventory/config.ts +117 -0
  447. package/src/inventory/group.ts +76 -0
  448. package/src/inventory/subjects.ts +114 -0
  449. package/src/inventory/walk.ts +129 -0
  450. package/src/labels/audit.ts +82 -0
  451. package/src/labels/coverage.ts +79 -0
  452. package/src/labels/map.ts +101 -0
  453. package/src/labels/phase.ts +95 -0
  454. package/src/markdown/bans.ts +94 -0
  455. package/src/markdown/files.ts +102 -0
  456. package/src/markdown/gate.ts +28 -0
  457. package/src/markdown/scan.ts +295 -0
  458. package/src/markdown/structure.ts +730 -0
  459. package/src/migrate/apply.ts +115 -0
  460. package/src/migrate/plan.ts +103 -0
  461. package/src/migrate/rename.ts +183 -0
  462. package/src/pr/bijection.ts +145 -0
  463. package/src/pr/paths.ts +335 -0
  464. package/src/process/harness.ts +167 -0
  465. package/src/project-root.ts +19 -0
  466. package/src/records/backup.ts +455 -0
  467. package/src/records/migrate.ts +78 -0
  468. package/src/records/size.ts +260 -0
  469. package/src/records/validate.ts +1162 -0
  470. package/src/sandbox/census.ts +228 -0
  471. package/src/sandbox/coverage.ts +115 -0
  472. package/src/sandbox/expect.ts +629 -0
  473. package/src/sandbox/tree.ts +47 -0
  474. package/src/secrets/marker.ts +30 -0
  475. package/src/secrets/patterns.ts +142 -0
  476. package/src/secrets/scan.ts +123 -0
  477. package/src/secrets/shipped.ts +111 -0
  478. package/src/seed-marker.ts +74 -0
  479. package/src/serve/static.ts +322 -0
  480. package/src/sessions/claim.ts +85 -0
  481. package/src/sessions/live.ts +79 -0
  482. package/src/sessions/registry.ts +137 -0
  483. package/src/sessions/resolve.ts +333 -0
  484. package/src/slides/layouts.ts +391 -0
  485. package/src/slides/open.ts +18 -0
  486. package/src/slides/parse.ts +84 -0
  487. package/src/slides/render.ts +88 -0
  488. package/src/slides/styles.ts +44 -0
  489. package/src/snippets/categories.ts +66 -0
  490. package/src/snippets/list.ts +32 -0
  491. package/src/snippets/presets.ts +50 -0
  492. package/src/standards/audit.ts +132 -0
  493. package/src/standards/read.ts +100 -0
  494. package/src/sync/check.ts +692 -0
  495. package/src/sync/engine.ts +576 -0
  496. package/src/sync/git.ts +225 -0
  497. package/src/sync/history.ts +123 -0
  498. package/src/sync/layout.ts +140 -0
  499. package/src/sync/reverse.ts +268 -0
  500. package/src/sync/seeds-report.ts +126 -0
  501. package/src/sync/stamp.ts +358 -0
  502. package/src/sync/target.ts +70 -0
  503. package/src/sync/workflow.ts +200 -0
  504. package/src/target.ts +43 -0
  505. package/src/targets/pulls.ts +250 -0
  506. package/src/targets/registry.ts +203 -0
  507. package/src/targets/resolve.ts +145 -0
  508. package/src/targets/sweep.ts +246 -0
  509. package/src/tasks/archive.ts +506 -0
  510. package/src/tasks/record.ts +311 -0
  511. package/src/tasks/trunk.ts +89 -0
  512. package/src/tasks/validate.ts +1001 -0
  513. package/src/teach/lesson.ts +180 -0
  514. package/src/teach/workspace.ts +842 -0
  515. package/src/tooling/gitignore.ts +122 -0
  516. package/src/tooling/inject.ts +193 -0
  517. package/src/tooling/list.ts +39 -0
  518. package/src/tooling/manifest.ts +176 -0
  519. package/src/tooling/package.ts +166 -0
  520. package/src/tooling/read.ts +65 -0
  521. package/src/tooling/scan.ts +147 -0
  522. package/src/tooling/stamp.ts +44 -0
  523. package/src/transcripts/fetch.ts +156 -0
  524. package/src/transcripts/metadata.ts +54 -0
  525. package/src/transcripts/vtt.ts +114 -0
  526. package/src/ui.ts +266 -0
  527. package/src/version/compare.ts +53 -0
  528. package/src/version/installed.ts +40 -0
  529. package/src/version/manager.ts +67 -0
  530. package/src/version/skew.ts +192 -0
  531. package/src/wiki/init.ts +85 -0
  532. package/src/worktree.ts +143 -0
  533. package/src/worktrees/reclaim.ts +306 -0
  534. package/standards/architecture.md +72 -0
  535. package/standards/branch.md +59 -0
  536. package/standards/commit.md +72 -0
  537. package/standards/context.md +151 -0
  538. package/standards/design.md +93 -0
  539. package/standards/diagrams.md +152 -0
  540. package/standards/glossary.md +75 -0
  541. package/standards/groundwork.md +211 -0
  542. package/standards/index.md +36 -0
  543. package/standards/intake.md +192 -0
  544. package/standards/issue.md +94 -0
  545. package/standards/markdown.md +137 -0
  546. package/standards/memory.md +144 -0
  547. package/standards/plan.md +172 -0
  548. package/standards/pr.md +139 -0
  549. package/standards/publish.md +51 -0
  550. package/standards/readme.md +208 -0
  551. package/standards/requirements.md +70 -0
  552. package/standards/rule.md +118 -0
  553. package/standards/session.md +109 -0
  554. package/standards/skill.md +300 -0
  555. package/standards/slug.md +39 -0
  556. package/standards/snippets.md +76 -0
  557. package/standards/standard.md +170 -0
  558. package/standards/tasks.md +254 -0
  559. package/standards/teach.md +153 -0
  560. package/standards/versioning.md +71 -0
  561. package/standards/wireframes.md +113 -0
  562. package/tooling/astro/configs/astro.config.mjs +31 -0
  563. package/tooling/astro/configs/eslint.config.js +79 -0
  564. package/tooling/astro/configs/playwright.config.ts +26 -0
  565. package/tooling/astro/configs/tsconfig.json +12 -0
  566. package/tooling/astro/configs/vitest.config.ts +22 -0
  567. package/tooling/astro/manifest.toml +34 -0
  568. package/tooling/astro/reference.md +60 -0
  569. package/tooling/base/configs/.editorconfig +5 -0
  570. package/tooling/base/configs/.github/pull_request_template.md +18 -0
  571. package/tooling/base/configs/.github/workflows/verify.yml +35 -0
  572. package/tooling/base/configs/.husky/commit-msg +1 -0
  573. package/tooling/base/configs/.husky/post-merge +61 -0
  574. package/tooling/base/configs/.husky/post-rewrite +21 -0
  575. package/tooling/base/configs/.husky/pre-commit +1 -0
  576. package/tooling/base/configs/.husky/pre-push +1 -0
  577. package/tooling/base/configs/.prettierrc +12 -0
  578. package/tooling/base/configs/.shellcheckrc +1 -0
  579. package/tooling/base/configs/.vscode/extensions.json +9 -0
  580. package/tooling/base/configs/.vscode/settings.json +3 -0
  581. package/tooling/base/configs/commitlint.config.js +11 -0
  582. package/tooling/base/configs/scripts/verify.sh +64 -0
  583. package/tooling/base/manifest.toml +30 -0
  584. package/tooling/base/reference.md +98 -0
  585. package/tooling/base/seeds/.claude/context/ci.md +33 -0
  586. package/tooling/base/seeds/.claude/context/development.md +37 -0
  587. package/tooling/base/seeds/.claude/context/index.md +11 -0
  588. package/tooling/base/seeds/.cspell/project-terms.txt +0 -0
  589. package/tooling/base/seeds/.cspell/tech-stack.txt +19 -0
  590. package/tooling/base/seeds/.lintstagedrc +8 -0
  591. package/tooling/base/seeds/.prettierignore +0 -0
  592. package/tooling/base/seeds/cspell.json +20 -0
  593. package/tooling/claude/manifest.toml +14 -0
  594. package/tooling/claude/reference.md +79 -0
  595. package/tooling/claude/seeds/.claude/ARCHITECTURE.md +13 -0
  596. package/tooling/claude/seeds/.claude/DESIGN.md +62 -0
  597. package/tooling/claude/seeds/.claude/REQUIREMENTS.md +18 -0
  598. package/tooling/claude/seeds/.claude/diagrams/index.md +8 -0
  599. package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +50 -0
  600. package/tooling/claude/seeds/.claude/hooks/memory-index.sh +68 -0
  601. package/tooling/claude/seeds/.claude/hooks/path-form.sh +57 -0
  602. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +52 -0
  603. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +86 -0
  604. package/tooling/claude/seeds/.claude/hooks/tasks-index.sh +71 -0
  605. package/tooling/claude/seeds/.claude/memory/index.md +8 -0
  606. package/tooling/claude/seeds/.claude/settings.json +47 -0
  607. package/tooling/claude/seeds/.claude/tasks/index.md +8 -0
  608. package/tooling/claude/seeds/.claude/wireframes/index.md +8 -0
  609. package/tooling/claude/seeds/CLAUDE.md +29 -0
  610. package/tooling/claude/user/settings.template.json +10 -0
  611. package/tooling/claude/user/statusline-command.sh +53 -0
  612. package/tooling/python/configs/.coveragerc +14 -0
  613. package/tooling/python/configs/.python-version +1 -0
  614. package/tooling/python/configs/mypy.ini +6 -0
  615. package/tooling/python/configs/pytest.ini +4 -0
  616. package/tooling/python/configs/ruff.toml +15 -0
  617. package/tooling/python/configs/scripts/verify.sh +77 -0
  618. package/tooling/python/manifest.toml +16 -0
  619. package/tooling/python/reference.md +66 -0
  620. package/tooling/python/seeds/.cspell/tech-stack.txt +19 -0
  621. package/tooling/python/seeds/tests/test_smoke.py +2 -0
  622. package/tooling/vite-react/configs/playwright.config.ts +26 -0
  623. package/tooling/vite-react/configs/tsconfig.json +35 -0
  624. package/tooling/vite-react/configs/vite.config.ts +24 -0
  625. package/tooling/vite-react/configs/vitest.config.ts +26 -0
  626. package/tooling/vite-react/manifest.toml +23 -0
  627. package/tooling/vite-react/reference.md +55 -0
  628. package/tooling/vite-react/seeds/.cspell/project-terms.txt +1 -0
  629. package/tooling/vite-react/seeds/.cspell/tech-stack.txt +1 -0
  630. package/tooling/web/configs/.github/workflows/verify.yml +134 -0
  631. package/tooling/web/configs/.vscode/extensions.json +13 -0
  632. package/tooling/web/configs/.vscode/settings.json +10 -0
  633. package/tooling/web/configs/e2e/home.spec.ts +6 -0
  634. package/tooling/web/configs/e2e/screenshot.ts +53 -0
  635. package/tooling/web/configs/eslint.config.js +82 -0
  636. package/tooling/web/configs/scripts/screenshot.sh +28 -0
  637. package/tooling/web/configs/scripts/verify.sh +80 -0
  638. package/tooling/web/configs/scripts/worktree-port.sh +74 -0
  639. package/tooling/web/configs/src/test/setup.ts +8 -0
  640. package/tooling/web/manifest.toml +58 -0
  641. package/tooling/web/reference.md +115 -0
  642. package/tooling/web/seeds/.cspell/tech-stack.txt +18 -0
  643. package/tsconfig.json +14 -0
@@ -0,0 +1,385 @@
1
+ ---
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
4
+ ---
5
+
6
+ # Install and sync
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 `.claude/rules/` and
13
+ never adds new ones. A rule the toolkit does not ship is left alone, which is
14
+ how project-authored rules survive, and one under `.claude/rules/project/`
15
+ is left alone regardless of its name, since that subfolder is project-authored
16
+ by location. It also removes a stale `.claude/GOV.md`
17
+ from the retired build. Use `canon gov install` to add rules.
18
+
19
+ A rule the toolkit finds no source for, sitting in a shared subdirectory rather
20
+ than under `.claude/rules/project/`, is reported with the path under that
21
+ subfolder it would take. The offer is conditional on the project having written
22
+ the rule, since a rule the toolkit shipped and later renamed reaches the same
23
+ line, and moving one there would mark it the project's for good. Nothing is
24
+ moved either way, because a rule's installed path is one the project's own
25
+ rules, skills, and docs may cite. `canon standards rule` carries the reserved
26
+ number bands behind that placement, where `900-999` is the range a
27
+ project-authored rule takes and everything below it belongs to the toolkit.
28
+
29
+ `canon sync --check` does not report an orphaned entry. It skips every one, so
30
+ the destination reaches `canon gov sync` alone, the one domain sync verb left.
31
+
32
+ When the target's install recorded a stack, `canon gov sync` also reports a
33
+ rule that stack lists and `.claude/rules/` does not hold, as a `missing` entry
34
+ carrying no change. This is what makes a target whose recorded sync point
35
+ postdates a rule joining its stack still see that rule: the report reads the
36
+ target's current entitlement against its current tree rather than diffing
37
+ from an anchor a later sync could advance past the rule's own commit. A
38
+ target whose install predates the recorded chain falls back to the same
39
+ band-inference `newRules` uses in `canon sync --check`.
40
+
41
+ There is no `canon standards sync` and no `canon standards install`. The corpus
42
+ installs into no project, so the domain has nothing in a target to reconcile.
43
+ `canon standards <name>` prints one, resolving `standards/` at the working root
44
+ and then the corpus inside the package, and `canon standards list --json` carries
45
+ the catalog.
46
+
47
+ There is no `canon snippets sync` and no `canon snippets install` either, on the
48
+ same ground: `claude/snippets` in the plugin cache symlinks live to the
49
+ toolkit's own `snippets/`, so a session reaches one at its `@` reference with
50
+ no copy to reconcile. `canon snippets list --json` carries the catalog and
51
+ `canon snippets create` is the one verb left that still writes a file.
52
+
53
+ ## Install guards
54
+
55
+ `canon gov install` requires its first argument under
56
+ `CANON_NON_INTERACTIVE=1`. It used to fall back to an interactive picker that
57
+ resolved to its first option headlessly, so a call with no stack installed
58
+ whichever stack sorted first. It now reports the valid names on stderr and
59
+ exits 1.
60
+
61
+ Every documented agent path already passes the argument, including
62
+ `canon init`. The confirm-then-apply prompt after it still resolves to `Yes`
63
+ headlessly, so a call that names its stack is unchanged.
64
+
65
+ `canon gov install` also refuses the toolkit root as a target. It resolves the
66
+ target before anything else, so a path that does not exist fails rather than
67
+ being scaffolded.
68
+
69
+ ## Standards resolution
70
+
71
+ `canon standards <name>` writes the document to stdout and the root it answered
72
+ from to stderr, so a caller capturing with `$(...)` receives the document alone.
73
+ A name resolves with or without its `.md` extension, and one that matches no
74
+ standard exits 1 after listing the catalog on stderr.
75
+
76
+ Two roots answer, in order: `standards/` at the working directory, then the
77
+ corpus inside the package. A project that authors standards of its own uses the
78
+ first, and this repository's own authoring root is the same path. `.claude/standards/`
79
+ is not among them, and no repository writes one any more. A copy an older
80
+ toolkit installed into a target resolves nothing.
81
+
82
+ There is no citation closure to compute, since nothing is copied. A standard
83
+ that hands a concern to a sibling names it in `Does not govern:` and a reader
84
+ runs the verb again for that name.
85
+
86
+ ## Governance regen
87
+
88
+ `canon gov regen` is the one governance verb that runs against the toolkit root,
89
+ because the `.claude/rules/` it writes there is produced output rather than an
90
+ operator's working copy. It reads the stack recorded in `internal/governance.toml`,
91
+ installs it alongside anything under `internal/rules/`, and clears the
92
+ destination first so a rule the record stopped naming disappears.
93
+
94
+ It takes `--root <path>` and defaults to the toolkit root, prints nothing on success, and
95
+ reports the reason on stderr with exit 1 when the record names a stack or rule
96
+ that does not resolve. `scripts/core/regen-claude-copies.sh` calls it, and the
97
+ Consumed copies stage of `bun run check` asserts the result is committed.
98
+
99
+ ## Whole-project sync
100
+
101
+ `canon sync` runs every installed domain sync, then offers to commit the result
102
+ and open a pull request. Under `CANON_NON_INTERACTIVE=1` it applies the domain
103
+ syncs and then refuses the git workflow, reporting the branch and commit it
104
+ would have created and exiting 0. Nothing is staged, committed, or pushed
105
+ headlessly.
106
+
107
+ Run it interactively to reach the commit and pull request options.
108
+ It also refuses a target whose working tree is dirty, so commit or stash first.
109
+
110
+ ## Drift reporting
111
+
112
+ `canon sync --check` reports drift and writes nothing, so it needs no clean tree
113
+ and is safe to run at any time. Each file is classified as `stale` when it still
114
+ matches what the toolkit installed, `customized` when the project edited it,
115
+ `stranded` when it sits at a path the toolkit no longer installs to, `orphaned`
116
+ when the project authored it, or `drifted` when no stamp covers it. Governance
117
+ also reports `missing`, for a rule the target's recorded stack lists that its
118
+ tree does not hold at all.
119
+
120
+ Use `--json` for the machine-readable report and `--exit-code` to fail a CI job. Orphaned
121
+ and missing files are both excluded from that exit code: a project-authored
122
+ rule never converges, and a sync that added a missing one silently changes
123
+ what the project is governed by, which stays a separate command an operator
124
+ chooses to run. Attribution reads `.claude/canon/config.json`, which every
125
+ install and sync writes.
126
+
127
+ A target installed before stamping shipped has no such file, and neither does
128
+ that fallback do anything to migrate it. A target stamped before the file
129
+ moved into `.claude/canon/` still carries it at the retired
130
+ `.claude/canon.json`, and `canon sync --check` reads that path when the current
131
+ one is absent, reporting it rather than moving it. Only a target carrying
132
+ neither path falls back to the toolkit's own git history.
133
+ Installed content matching any
134
+ version that history ever published proves the file is untouched, so it reports
135
+ `stale` naming the commit it came from, and content matching no published
136
+ version stays `drifted`. A toolkit reached outside a git clone, which is what a
137
+ registry install is, cannot run that fallback and reports
138
+ `historyUnavailable` alongside the unattributed files.
139
+
140
+ Each domain carries its own toolkit anchor in that file, so syncing one domain
141
+ never advances the revision another measures from, and each reports the upstream
142
+ commits touching its own source path. The `covers` field names the domains a
143
+ target has actually stamped, so a domain that was never stamped is legible
144
+ rather than reading as a clean one.
145
+
146
+ ### Tooling
147
+
148
+ Tooling records the stack chain an install resolved rather than per-file hashes,
149
+ since `src/tooling/` runs its own inject machinery and has no walk to attribute.
150
+ The chain is ordered nearest stack first, which is what a `--skip` run needs:
151
+ recording the leaf alone would send the next report measuring against a layer the
152
+ target deliberately does not carry. The report loads exactly those stacks, scans
153
+ them the way `canon tooling sync` would, and counts what differs per category
154
+ under `tooling.counts`.
155
+
156
+ `measured` is the field the section exists for. A target carrying no chain
157
+ reports `measured: false`, which separates tooling nobody has ever looked at from
158
+ tooling that is current. Both produce zero changes otherwise. Every target
159
+ installed before the record shipped starts unmeasured and leaves on its next
160
+ `canon tooling sync`, since backfilling would mean inferring the chain from
161
+ installed files, which is the guess the record replaces.
162
+
163
+ A workspace root records nothing, because each package resolves its own chain and
164
+ one written at the root would be that same guess. Run the check against a package
165
+ to measure it. Tooling never counts toward `--exit-code`, on the grounds seeds
166
+ are already excluded on, since it reports golden configs a project is expected to
167
+ edit and a job counting those stays red with no remedy.
168
+
169
+ ### Surfaces reported beside the domains
170
+
171
+ Six sections sit outside the per-domain scan, because each names something
172
+ that walk cannot see: `seeds`, `superseded`, `unmigrated`, `newSkills`,
173
+ `newRules`, and `reverse`. None of them produces a change, and no sync command
174
+ applies any of them. The headings below group the first three together and give
175
+ `newSkills` its description under `newRules`, which answers the same question
176
+ about a different corpus.
177
+
178
+ All six report only against a toolkit-managed target, which is one carrying a
179
+ `.claude/` directory, a `CLAUDE.md`, or a domain still at the root layout. The
180
+ report says so through `managed` in the JSON and routes an unmanaged directory to
181
+ `canon init`. Seeds are why the gate exists, since they enumerate from the toolkit
182
+ source rather than from what a target installed, so an unmanaged directory would
183
+ otherwise report every seed as `missing`.
184
+
185
+ A root-layout domain counts as a marker on its own, because the detection fires
186
+ only on root files the toolkit ships and a project in the old layout is one the
187
+ toolkit installed. When `managed` is false every section comes back empty rather
188
+ than the render alone going quiet, so a consumer reading `--json` never acts on a
189
+ finding the rendered half withheld.
190
+
191
+ #### Seeds, superseded artifacts, and unmigrated domains
192
+
193
+ `seeds` classifies every seed the toolkit ships against the target's copy, as
194
+ `matching`, `stale`, `drifted`, or `missing`. `missing` has no per-domain
195
+ equivalent, since the domain walk lists what a target installed and cannot see a
196
+ file that never arrived. There is no `customized` verdict here, because that one
197
+ needs a stamp and seeds carry none, so a file history cannot attribute stays
198
+ `drifted`. Reconcile the section with `claude-seed-sync`, which merges one
199
+ section at a time rather than replacing a file the project edits.
200
+
201
+ A markdown seed installs rewritten rather than copied, since the `stub: true`
202
+ marker the toolkit's own seed gate reads is stripped on the way in. The
203
+ comparison above runs against what the install would write, so a marked seed a
204
+ target never touched still reports `matching`. Every other seed copies byte for
205
+ byte.
206
+
207
+ `superseded` names a file a newer seed folder replaced, such as `.claude/TASKS.md`
208
+ against the `.claude/tasks/` that now ships. The entry carries `replacedBy` and
209
+ nothing else, and the file is never deleted, since the content belongs to the
210
+ project and only its author can decide where it moves. The list derives from the
211
+ seed tree rather than from a fixed set of filenames, so a folder added later is
212
+ covered without a code change. Only an exact stem matches, which leaves a
213
+ suffixed variant such as `TASKS-ARCHIVE.md` unreported.
214
+
215
+ Route it to `migration-superseded`. That skill resolves the standard governing
216
+ `replacedBy` from the `appliesTo` the standards catalog declares, reads the
217
+ destination shape with `canon standards <name>`, and proposes the split without
218
+ writing. Where a folder has no governing standard, the entry earns a named
219
+ refusal rather than a shape nobody stated.
220
+
221
+ `unmigrated` names a domain sitting at the root layout an older toolkit installed
222
+ to, with nothing at the path the current one reads. It carries `rootPath`,
223
+ `installPath`, and a file count. Without it a project holding `standards/` at its
224
+ root reports zero entries for that domain and reads as clean, which is the most
225
+ misleading state the report can produce.
226
+
227
+ The field currently names no domain. Standards and snippets are the two the
228
+ toolkit ever installed at a project root, and both closed their install channel,
229
+ so a project still holding a root `standards/` or `snippets/` folder is carrying
230
+ its own authoring surface rather than an unfinished install. Nothing proposes
231
+ moving either, and no command relocates the content. Move it yourself.
232
+
233
+ #### Rules the target never received
234
+
235
+ `newRules` names a rule the target could receive and its tree does not hold. A
236
+ sync refreshes the files a target already holds and adds none, so without this
237
+ section a project's rule set freezes at its install date while every file it
238
+ does hold reports as current. That is the report's most confident wrong
239
+ answer, since a clean result reads as a target holding everything the toolkit
240
+ publishes.
241
+
242
+ The list rides beside `newSkills`, which asks the same question about the plugin
243
+ catalog. Both are names rather than paths, and neither queues a change, because
244
+ the two remedies differ: a skill loads live from the plugin directory and needs
245
+ nothing run, while a rule reaches a target only when someone runs
246
+ `canon gov install`.
247
+
248
+ Read a clean section as reporting rather than as delivering. Nothing here
249
+ installs, nothing counts toward `--exit-code`, and a target can read the list and
250
+ act on none of it. The value stops at an operator reading it, which is the same
251
+ contract the skills list already sets.
252
+
253
+ Since `canon gov install` records the stack it resolved, a target carrying that
254
+ record answers this by comparing its current entitlement against its current
255
+ tree, with no anchor and no git diff involved. That is what lets the section
256
+ name a rule that shipped before the target's last sync: the anchor a sync
257
+ advances plays no part in the read, where an anchor-bound diff can never see a
258
+ rule on the far side of a window a later sync moved past it. The per-file
259
+ `missing` state `canon gov sync` reports comes from the same comparison, so a
260
+ rule the chain lists reaches both surfaces the same way.
261
+
262
+ A target stamped before governance recorded a chain falls back to the older
263
+ band-inference read below. The measurement there anchors on governance's own
264
+ stamp rather than on the oldest anchor across domains, since rules are
265
+ domain-scoped and a shared anchor would let another domain's sync move the
266
+ revision rules are measured from. A target carrying no chain and no governance anchor
267
+ reports nothing at all: it has no date to measure against, and diffing from the
268
+ start of history would read the whole catalog as new.
269
+
270
+ An anchor this toolkit cannot resolve reports nothing by the same route, and that
271
+ one is not visible. A stamp naming a revision the running clone has never seen,
272
+ which is what a registry install without history or a shallow clone produces,
273
+ fails the read and yields an empty list rather than a stated absence. It looks
274
+ identical to a target holding every rule the toolkit publishes. `newSkills`
275
+ behaves the same way, and neither carries the `historyUnavailable` flag the
276
+ per-domain scan uses to separate the two. Treat an empty section on a toolkit
277
+ that is not a full clone as unmeasured rather than clean. This gap does not
278
+ reach the chain-based read above, since it consults no anchor at all.
279
+
280
+ In the fallback, entitlement is filtered, because a stack does not receive
281
+ every rule. The base stack takes the `core` and `claude` folders whole and
282
+ every other stack extends it with individually named rules, so an unfiltered
283
+ list would tell a base consumer about rules it can never receive and train the
284
+ reader to skip the section.
285
+
286
+ The filter accepts a band on either of two grounds. A folder the base stack takes
287
+ whole is entitled to every target, read from the stack file so a folder added to
288
+ base later needs no code change. Every other band is read off the folders the
289
+ target already carries, which is the fallback's only evidence of what a target
290
+ was entitled to before it recorded a chain. One band can be reached by more than
291
+ one stack, so the test over-reports inside a folder the target holds, which
292
+ costs a line where under-reporting would cost the section its point.
293
+
294
+ A rule the target already holds is dropped by name. That is what keeps a rule the
295
+ toolkit moved between band folders out of the list, since a rename reaches this
296
+ read as an addition and only the name tells the two apart.
297
+
298
+ This closes the dangling-citation case as a side effect rather than checking for
299
+ one. The live instance is a rule citing a sibling authored after the target's
300
+ install, and the section names the sibling as new rather than naming the citation
301
+ as broken. That is enough for an operator to act on, and it is not a citation
302
+ check.
303
+
304
+ #### The reverse walk
305
+
306
+ `reverse` is the one section built by walking the target rather than the
307
+ catalog. Every other surface enumerates toolkit-owned keys and asks whether the
308
+ target matches, so a folder the toolkit deleted appears in none of them. It
309
+ carries `unclaimed`, `migrations`, and `historyUnavailable`.
310
+
311
+ `unclaimed` names a folder the target holds at a top-level path the toolkit once
312
+ shipped and has since deleted. The candidate roots come from the toolkit's own
313
+ history rather than from a list, so a root dropped later is covered without a
314
+ code change. Scoping to those roots is what keeps the walk useful: walking the
315
+ whole tree reports every project folder as unclaimed, which is true and says
316
+ nothing.
317
+
318
+ The managed gate above applies here too, and it is the one place it surprises.
319
+ A directory holding a dropped folder and nothing else reports an empty `reverse`
320
+ rather than the folder, because it carries none of the three markers. Read an
321
+ empty section on an unmanaged target as a walk that never ran rather than as a
322
+ clean result.
323
+
324
+ Each entry carries `rel`, a file count, and an `attribution` of `dropped`,
325
+ `project`, or `unattributed`. A dropped folder and one the project wrote are the
326
+ same bytes at the same path, so the verdict is traced from history rather than
327
+ guessed from the filesystem.
328
+
329
+ Content matching a version the toolkit published reads as `dropped` and carries
330
+ the `since` commit that published it. Names the toolkit shipped holding content
331
+ it never published read as `unattributed`, which is a state in its own right
332
+ rather than a soft yes. No overlap at all reads as `project`, and the render
333
+ drops those while the JSON keeps them.
334
+
335
+ Only files whose path the toolkit once held are hashed, so a project folder
336
+ colliding on a retired name costs the walk no reads. The cost is that a file the
337
+ toolkit shipped and the target renamed goes unmatched, the same limit the
338
+ `unmigrated` count carries.
339
+
340
+ `migrations` names a proposal-only skill with a live case in this target. It
341
+ fires on a `CLAUDE.md` past 250 lines for `migration-claude-md`, and on a `docs/` folder
342
+ holding markdown with no populated `.claude/context/` for `migration-context`.
343
+ Each entry carries the skill name and the measurement behind it, so a consumer
344
+ can check the proposal before running it. Without the field both skills are
345
+ documented and unreachable from any report.
346
+
347
+ #### What counts toward the gate
348
+
349
+ `unmigrated` counts toward `--exit-code`, since running the relocation closes it.
350
+ `superseded` and every seed state are excluded, for the reason `orphaned` already
351
+ is: only the user can move content they wrote, so failing a job on it leaves the
352
+ job red with no mechanical remedy.
353
+
354
+ `newRules` is excluded on a different ground, since a command does close it. What
355
+ excludes it is that installing a rule changes what a project is governed by, so
356
+ gating on the count would pressure a target into adopting rules nobody chose.
357
+ `newSkills` is excluded because it needs no command at all.
358
+
359
+ The whole `reverse` section is excluded on the same grounds, and more strongly.
360
+ Every entry in it is a judgment about a file the project may own, and one of its
361
+ three verdicts is a labelled unknown by design. The unmigrated detection shipped
362
+ that exact false positive once, failing a push with no action that cleared it,
363
+ so this section reports and gates nothing.
364
+
365
+ ## Bootstrap
366
+
367
+ `canon init` installs up to four core domains and reports each one independently. A
368
+ domain that fails does not abort the run, so the command finishes the rest and
369
+ exits 1 naming the failures. Passing any flag skips the confirmation prompt,
370
+ which is what makes it scriptable.
371
+
372
+ `--stack` defaults to `base`, and the default
373
+ does not read as a passed flag, so a bare `canon init` installs governance and
374
+ still prompts. `--skip` takes `wiki` and `governance`, and warns
375
+ without aborting on any other value. There is no `--standards` and no
376
+ `--snippets`, since no run writes either corpus into the target.
377
+
378
+ ## Unguarded tooling primitives
379
+
380
+ `canon tooling inject` and `canon tooling prune-gitignore` are the unguarded
381
+ primitives beneath `sync`. They apply one stack with no scan and no prompt, and
382
+ they deliberately skip the check that rejects `claude`, which is how `canon
383
+ claude` drives its own stack through them. Use `sync` unless you are scripting
384
+ provisioning. Both frame their own output, so pass `--nested` when calling from
385
+ inside an already-open frame.
@@ -0,0 +1,81 @@
1
+ ---
2
+ title: Intake
3
+ description: Reading intake folder counts and items, the three read states an item can be in, landing a batch of selections in one cluster, the refusal reasons, and why a call is scoped to one file
4
+ ---
5
+
6
+ # Intake
7
+
8
+ ## List
9
+
10
+ `canon intake list` reports the intake folders under `.claude/intake/`, or the items one folder holds. It reads and never writes, because an answer belongs to the operator and a verb that filled one would decide what the folder exists to ask.
11
+
12
+ A folder carries a two-digit ordinal ahead of its slug, as in `21-toolkit-overview`, so a listing sorts by when each opened. A bare slug still resolves: passing `toolkit-overview` matches the one folder whose name is an ordinal ahead of it, and the folder's real name is what every command reports back.
13
+
14
+ ```bash
15
+ canon intake list
16
+ canon intake list toolkit-overview --unread --json
17
+ ```
18
+
19
+ | Option | Behavior |
20
+ | --------------- | ------------------------------------------------------------------------- |
21
+ | `--unread` | Keep only what is unread: folders carrying one, or a folder's empty slots |
22
+ | `--json` | Emit a machine-readable record on stdout |
23
+ | `--root <path>` | Intake root, defaulting to the main worktree |
24
+
25
+ With no slug it reports per-folder counts. With one it reports every item grouped by the cluster file holding it, each carrying its label, title, open question, suggestion, and answer.
26
+
27
+ An item sits in one of three read states, and the counts keep them apart:
28
+
29
+ | State | What it means |
30
+ | --------- | ------------------------------------------------------- |
31
+ | unread | The slot is present and empty, so nobody has reached it |
32
+ | answered | The slot carries text, which is a decision already made |
33
+ | malformed | The item carries no slot, so no verb here can answer it |
34
+
35
+ The third is counted apart from both rather than folded into either. Counted as answered it hides a file that needs fixing behind a folder reading as fully worked through, and counted as unread it joins a list whose every entry `answer` then refuses.
36
+
37
+ The index is skipped, since it points at items and answers nothing itself. So is any item a file displays inside a fence, which is how a folder copying the item format into its own overview is read as the sample it is rather than as an item offering a slot no reader owns.
38
+
39
+ ## Answer
40
+
41
+ `canon intake answer` writes selections into the answer slots of one cluster file.
42
+
43
+ ```bash
44
+ canon intake answer toolkit-overview --cluster 05-coverage.md --set 3=ok
45
+ canon intake answer toolkit-overview --cluster 11-intake-skill.md --set 3d=ok --set 9="not worth it" --json
46
+ ```
47
+
48
+ | Option | Behavior |
49
+ | --------------------- | ----------------------------------------------------- |
50
+ | `--cluster <file>` | Cluster file the items live in, with or without `.md` |
51
+ | `--set <item=answer>` | Answer to land on an item, repeatable |
52
+ | `--json` | Emit a machine-readable record on stdout |
53
+ | `--root <path>` | Intake root, defaulting to the main worktree |
54
+
55
+ Items are labeled per cluster file, so a selection names the cluster and the label together. A label alone names an item in every cluster at once.
56
+
57
+ A label may carry a letter suffix, as in `3a` beside `3`, which is how a pass records a finding split after the fact rather than renumbering every item below it. Pass it exactly as the heading spells it.
58
+
59
+ One call writes one cluster. A call per selection is the alternative, and several of those against the same file read it before any of them writes, so every answer but the last is lost with nothing reporting it. The selections split on the first `=`, so an answer carrying its own survives intact.
60
+
61
+ An item already carrying an answer refuses rather than being overwritten, and one filled item refuses the whole batch, so a partly applied write never lands. Drop the named item and send the rest.
62
+
63
+ Exit codes: `0` every named item now carries its answer, `1` refused. The `reason` field carries `no-intake`, `no-folder`, `ambiguous-slug`, `no-cluster`, `no-item`, `answered`, or `bad-input`. A bare slug matching more than one ordinal-prefixed folder refuses as `ambiguous-slug` rather than `no-folder`, naming every match in `detail`.
64
+
65
+ An exit code says nothing about a call made from a session, since a shell profile may wrap the binary in a function taking its status from a later command. Read the record's `reason` rather than the exit when a skill consumes this, which matters most here because the verb writes.
66
+
67
+ `bad-input` covers a malformed command line: no cluster, no selection, a selection that parses to no label and answer, an empty answer, an answer carrying a line break, or two answers for one item. It is separate from the reasons describing the folder, so a caller that mistyped a flag is not sent to repair a file that is fine.
68
+
69
+ An empty answer refuses rather than writing an empty slot. The slot means unread while it is empty, so writing one back would report an item as answered that nobody decided.
70
+
71
+ An answer occupies one line, and one carrying a line break refuses before anything is read. Writing it splices a bare continuation into the item that matches none of the patterns the reader tests, so the slot reads back as the text before the break while the item counts as answered, and the refusal on an already-answered item then leaves hand-editing the file as the only correction. The whole batch refuses, so a good selection beside a broken one never lands half applied.
72
+
73
+ The folder is shared scratch at the main worktree root, so `--root` defaults to the first entry of `git worktree list` rather than the working directory. The write is an edit inside a file that already exists, which `Edit` and `Write` refuse from a linked worktree and a shell stream editor may not do, so the verb resolves the root in-process and rewrites whole lines.
74
+
75
+ Skills branch on the reason rather than on the exit code:
76
+
77
+ ```bash
78
+ canon intake list toolkit-overview --unread --json | jq -r '.clusters[] | .cluster'
79
+ ```
80
+
81
+ For the folder layout, the item format, the answer contract, and retrieval, see `standards/intake.md`.
@@ -0,0 +1,103 @@
1
+ ---
2
+ title: Key Changes bijection
3
+ description: Comparing the files a pull request body's Key Changes names against its own diff, the two directions and why they are graded apart, the span rules the extractor was measured into, and the three refusals that separate a clean pass from a read that produced nothing
4
+ ---
5
+
6
+ # Key Changes bijection
7
+
8
+ `canon pr key-changes` reads the `## Key Changes` section of a pull request body, lifts the paths its bullets claim, and compares that set against the files the pull request actually changed. This repository squash-merges, so the body becomes the commit message and the record on the trunk once the branch is gone.
9
+
10
+ ```bash
11
+ canon pr key-changes
12
+ canon pr key-changes 1265 --json
13
+ canon pr key-changes --body .claude/.tmp/pr/body.md --base origin/main
14
+ ```
15
+
16
+ The positional is the pull request to read, defaulting to the one open on this branch.
17
+
18
+ | Option | Behavior |
19
+ | --------------- | ------------------------------------------------------------------ |
20
+ | `--body <path>` | Read the body from a file, taking the changed set from git instead |
21
+ | `--base <ref>` | Far side of the range when `--body` supplies the body |
22
+ | `--root <path>` | Repository to read, defaulting to the cwd |
23
+ | `--json` | Add a machine-readable record on stdout, keeping the frame |
24
+
25
+ `--body` decides where both halves come from, so a number passed beside it is never read. The body comes off disk and the changed set from the local range, which is the shape a fixture and a body still being drafted both need.
26
+
27
+ Without `--body` the body, the file list, and the head commit come back from one `gh pr view` call, because the three have to describe the same commit and reading them apart leaves a window where a push between them compares a body against another head's files.
28
+
29
+ That view caps its file list at 100 rows and says nothing about having done so, which was measured against `#1250`: the pull request carries 101 files and the view reports 100. A pull request at the cap therefore takes a second read through the paginated endpoint, and a failure there refuses with `gh-truncated` rather than comparing against a set known to be short. A set silently one file short is the worst input this takes, since the missing file is exactly what a correct bullet would then be accused of inventing.
30
+
31
+ ## The two directions
32
+
33
+ They are reported apart because they want different tolerances.
34
+
35
+ - **`unmet`** is a whole path the body claims and the diff does not carry. This is the graded direction and it sets the exit code. A bullet naming an untouched file is wrong more often than not, and it corrupts the record that reaches the trunk.
36
+ - **`unnamed`** is a changed file no bullet reached. Reported with no grade, since the class covers a real omission and equally a lockfile, a generated asset, or a regenerated index that earns no bullet. Grading it would fire on nearly every branch.
37
+ - **`unresolved`** is a path written partially, such as `claude-worker/SKILL.md` for a file under `claude/skills/`. It is judged in neither direction.
38
+
39
+ A partial path can credit a changed file and can never accuse one. Nothing separates a path written short from a path written wrong, so the asymmetry keeps the accusing direction to spans the tree can anchor.
40
+
41
+ ## What counts as a claim
42
+
43
+ Only `## Key Changes` is read. `## Technical Context` legitimately names files a branch never touched, such as an install stamp inside a target, so widening the read manufactures a finding out of every argument an author made for the change.
44
+
45
+ Inside the section, the claim region of a bullet ends at its first comma outside a backticked span. That one lever was chosen by measurement. Over the 23 merged pull requests in this repository carrying the section, reading whole bullets reported 16 paths as claimed-but-untouched and every one was a file the body named for context. Cutting at the comma left 110 claims of the original 149 and took the false reports to 2. A list of sixteen clause-opening words tried beside it removed nothing the comma had not already removed, because this corpus punctuates every one of them.
46
+
47
+ A span inside the claim region has to survive all of these:
48
+
49
+ | Rule | What it keeps out |
50
+ | ------------------------------------------------- | ------------------------------------------------------ |
51
+ | No whitespace, `<`, `>`, `$`, `*`, `\|`, `?`, `^` | A backticked command, a placeholder, a glob, a pattern |
52
+ | No `://` and no leading `/`, `~`, `@`, `#`, `!` | A URL, an absolute path, a module alias |
53
+ | Contains `/` | A bare filename with no folder around it |
54
+ | Extension starts with a letter, or ends with `/` | A dotted number such as an address |
55
+ | Not a single top-level folder | `src/`, which nobody claims to have rewritten whole |
56
+ | A `file:line` span leads its bullet | A citation into a file the bullet is describing |
57
+ | The region carries no no-change marker | A bullet recording a file it deliberately left alone |
58
+
59
+ The last three rows earn their place from the corpus rather than from a rule. Every body that spelled a lone top-level folder was naming where something lives, and a line citation that follows another claim in the same bullet points into the file being described rather than naming a second one. A line citation leading its bullet stays a claim, which is how a body names the exact line it rewrote.
60
+
61
+ The no-change marker is the one rule the region cut cannot substitute for. A body writes "Leave `x` untouched, since the decision keeps it" to record a change it declined, and the path sits ahead of the first comma, so a stricter cut would not reach it and a looser one would find more. Since `keep` and `leave` each open a real claim often enough, the marker decides it rather than the leading verb. Three words carry the set: `untouched`, `unchanged`, and `as written`. `in place` was measured and dropped, because rewriting a file in place is an ordinary claim, and `no other line` was dropped because a correct bullet writes "as one insertion that touches no other line" about a change it did make. `alone` shipped in the set too, until review found every corpus occurrence sitting past the first comma, where the cut already excludes it, so the word voided no true claim there. Restrictive use is the more common one in this repository's own prose, and a comma-free bullet exposed it: "Move the threshold read into `src/gate/stages.ts` alone" asserts an edit, and the marker voided it while the word was still in the set. Over the 40-pull-request corpus the rule still voids no true claim.
62
+
63
+ A folder claim covers every changed file beneath it. A bare filename drops outright: resolved as a sibling of a path earlier in its bullet it produced seven wrong paths across this corpus against two right ones, because a compound bullet names a sibling folder as often as a sibling file.
64
+
65
+ ## Anchoring
66
+
67
+ A claim is anchored when its first segment names a folder the tree holds. The roots come from the tracked file list joined with the first segment of every changed path, and the second half is what admits a folder the branch created. Reading the tree alone would mark every claim under a new top-level directory unanchored, and an unanchored claim never accuses, so the first branch to open one would lose the graded direction with nothing reporting it.
68
+
69
+ ## Exit codes and refusals
70
+
71
+ | Code | Meaning |
72
+ | ---- | ------------------------------------------------- |
73
+ | `0` | every claimed path is in the diff |
74
+ | `1` | refused, with `reason` naming the cause |
75
+ | `2` | at least one claimed path is absent from the diff |
76
+
77
+ Branch on the record rather than on the exit code. A shell function wrapping `canon` takes its status from whatever it runs last, so every non-zero exit can reach a caller as zero.
78
+
79
+ Three refusals separate a clean pass from a read that produced nothing:
80
+
81
+ - `no-section`. The body carries no such heading, so it claims nothing.
82
+ - `no-claims`. The heading is there and no span resolved, which is the extractor failing over prose rather than the body being wrong. Nothing is raised.
83
+ - `no-changes`. The pull request changed no files, so no claim has anything to answer.
84
+
85
+ An empty extraction read as a clean pass is the failure shape this repository has recorded twice, which is why the middle one is its own reason rather than a zero count.
86
+
87
+ `gh-missing`, `gh-failed`, `gh-truncated`, `unreadable-body`, `unreadable-tree`, `no-base`, `bad-base`, and `unreadable-changes` cover the reads that never reached a comparison.
88
+
89
+ ## What it was measured against
90
+
91
+ Driven over the 40 most recent merged pull requests in this repository, 23 of which carry the section, the shipped verb reported zero unmet paths and one `no-claims` refusal. The naive extractor it replaced reported 16.
92
+
93
+ The first body written after that corpus closed produced two, which is what the no-change marker and the open class above come from. One was the disclaiming bullet and the rule now covers it at no cost to the corpus. The other is the definition-site class, left open with the report naming it.
94
+
95
+ The bound is that the corpus is one repository writing to one house style. A project that punctuates differently loses claims to the comma cut rather than gaining false ones, since every rule above drops rather than invents, so the check degrades toward silence rather than toward noise.
96
+
97
+ What it cannot see is a bullet claiming a change to a file the branch did touch for another reason, a path written without backticks, and a claim in a second coordinated clause after the comma. The last of those falls to the ungraded direction rather than out of the reading.
98
+
99
+ One class stays open and is named rather than closed. A bullet can cite where something is defined while claiming an edit somewhere else, as `#1274` does with "Name the slug transform in `standards/slug.md` inline in the same step". The path is a definition site, the edit target is the step, and separating the two needs the sentence parsed rather than cut. A finding on such a bullet is this class rather than a stale claim, and a reader weighing an `unmet` path checks whether the bullet's real target is a locative the path does not name.
100
+
101
+ ## Where it runs
102
+
103
+ `claude-pr-review` Step 3 calls it and files an `unmet` path as a `should-fix` finding under the `**PR body**` block the stale ticked box already takes, since what both corrupt is the merge record rather than a file in the diff. A body is edited between pushes, so a finding names the head the comparison ran at.
@@ -0,0 +1,73 @@
1
+ ---
2
+ title: Label coverage
3
+ description: Reading a changed set against the pull request label map, the two tables it matches, how a gap is separated from a decision, and why an absent map is an answer
4
+ ---
5
+
6
+ # Label coverage
7
+
8
+ `canon labels audit` resolves the paths a branch changed against the label map a project declares, reports the labels the set earns, and names every path no row reaches. It closes a gap the map's own comment had predicted since the map shipped: nothing detected a surface added after the rows were written, so such a branch merged carrying no label and nobody heard about it.
9
+
10
+ ```bash
11
+ canon labels audit
12
+ canon labels audit --json
13
+ canon labels audit --base origin/main
14
+ canon labels audit src/cli.ts docs/index.md --json
15
+ ```
16
+
17
+ | Option | Behavior |
18
+ | --------------- | ----------------------------------------------------------------- |
19
+ | `--base <ref>` | Far side of the range, defaulting to the merge base against trunk |
20
+ | `--root <path>` | Repository to read, defaulting to the current directory |
21
+ | `--json` | Add a machine-readable record on stdout, keeping the frame |
22
+
23
+ Positional paths replace the range entirely. A caller holding a changed set already passes it rather than paying for a second git read, and the record then omits `base` to say the range was never resolved.
24
+
25
+ Under `--json` the record holds stdout alone and the frame still renders on stderr, refusals included, which is the split `output-shape.md` fixes for every mode.
26
+
27
+ ## The map it reads
28
+
29
+ The map sits at `.claude/canon/pr-labels.toml` and carries two tables. `[domains]` keys a label name to the path prefixes that earn it, and `[declined]` keys a reason to the prefixes that earn no label on purpose.
30
+
31
+ ```toml
32
+ [domains]
33
+ api = ["services/api/"]
34
+ web = ["apps/web/", "packages/ui/"]
35
+
36
+ [declined]
37
+ release-managed = ["CHANGELOG.md", "package.json"]
38
+ generated = ["build/manifest.json"]
39
+ ```
40
+
41
+ Matching is prefix-anchored, so a row written for an authoring root reaches nothing under the copy a project consumes. A path claimed by rows in both tables takes the label, since it already has a subject and reporting it as deliberately unlabelled would contradict the label the same run applies.
42
+
43
+ ## A gap and a decision are different findings
44
+
45
+ The verb sorts every changed path into three buckets and keeps two of them apart:
46
+
47
+ - **Labelled.** At least one `[domains]` row reaches the path. It contributes its label and nothing else.
48
+ - **Declined.** A `[declined]` row reaches it. Somebody already decided the path earns no label, so the run names it and moves no exit code.
49
+ - **Uncovered.** Neither table reaches it. This is the finding, and it wants either a prefix on the row that owns its subject or a `[declined]` row carrying the reason.
50
+
51
+ Folding the last two together was the shape this replaced. A report that cannot tell a surface nobody has gotten to from one somebody refused is useful about neither, which is why the declined half moved out of the map's trailing comment and became data the command reads.
52
+
53
+ ## The range it reads
54
+
55
+ The default range is the branch against the trunk, resolved as the merge base against `origin/main` and then local `main`. The changed set is that base diffed against the working tree, plus untracked files git does not ignore, rather than against `HEAD`.
56
+
57
+ Reading the working tree is what lets the check run before the branch commits, which is the moment a session adds the surface nobody covered. The set is a superset of `base..HEAD`, so a caller running after the commits still sees the whole branch.
58
+
59
+ ## Exit codes and refusals
60
+
61
+ | Code | Meaning |
62
+ | ---- | ---------------------------------------------- |
63
+ | `0` | every changed path is labelled or declined |
64
+ | `1` | refused, with `reason` naming the cause |
65
+ | `2` | at least one changed path is reached by no row |
66
+
67
+ It reports and never gates. Whether an uncovered surface deserves a label is a judgment only whoever owns that surface can make, and a push failing on one teaches a contributor to route around the check while nothing about the surface has changed.
68
+
69
+ A project declaring no map refuses with `no-map`, which is an answer rather than a fault. Such a project is labelled silently by design, and treating the absence as a break would make the map mandatory for every target. `canon audits run` reads that one reason as an expected absence and every other refusal as a measure that did not run.
70
+
71
+ ## What it does not measure
72
+
73
+ A prefix reaching no path is invisible here. A row left behind by a deleted folder stays in the map forever, and this verb has nothing to say about it. That is the map going stale from the other side and a second measure rather than this one.