@erclx/canon 5.32.0 → 5.34.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 (250) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/api-design/REQUIREMENT.md +1 -2
  3. package/claude/skills/api-design/SKILL.md +2 -3
  4. package/claude/skills/api-design/references/adopted.md +1 -1
  5. package/claude/skills/auto-ship/SKILL.md +1 -1
  6. package/claude/skills/auto-ship/references/worktree-name.md +1 -1
  7. package/claude/skills/backlog-triage/REQUIREMENT.md +2 -2
  8. package/claude/skills/backlog-triage/SKILL.md +1 -1
  9. package/claude/skills/canon-cli/REQUIREMENT.md +16 -13
  10. package/claude/skills/canon-cli/SKILL.md +75 -32
  11. package/claude/skills/canon-feedback/REQUIREMENT.md +1 -0
  12. package/claude/skills/canon-feedback/SKILL.md +11 -29
  13. package/claude/skills/canon-feedback/references/template.md +53 -0
  14. package/claude/skills/{canvas → canvas-board}/REQUIREMENT.md +2 -2
  15. package/claude/skills/{canvas → canvas-board}/SKILL.md +6 -3
  16. package/claude/skills/{canvas → canvas-board}/references/canvas.md +26 -3
  17. package/{standards → claude/skills/canvas-board/references}/design.md +11 -2
  18. package/claude/skills/code-craft/REQUIREMENT.md +1 -1
  19. package/claude/skills/code-craft/SKILL.md +0 -1
  20. package/claude/skills/context-fold/REQUIREMENT.md +25 -33
  21. package/claude/skills/context-fold/SKILL.md +25 -70
  22. package/claude/skills/context-fold/references/classify.md +3 -3
  23. package/claude/skills/context-fold/references/completion-report.md +4 -4
  24. package/claude/skills/context-fold/references/context-refresh.md +5 -5
  25. package/{standards → claude/skills/context-fold/references}/context.md +16 -5
  26. package/claude/skills/context-fold/references/unusable-baseline.md +3 -5
  27. package/claude/skills/create-rule/SKILL.md +3 -3
  28. package/{standards → claude/skills/create-rule/references}/rule.md +15 -6
  29. package/claude/skills/create-skill/SKILL.md +2 -2
  30. package/{standards → claude/skills/create-skill/references}/skill.md +18 -15
  31. package/claude/skills/design-taste/REQUIREMENT.md +3 -3
  32. package/claude/skills/design-taste/SKILL.md +1 -1
  33. package/claude/skills/docs-sync/REQUIREMENT.md +1 -1
  34. package/claude/skills/docs-sync/SKILL.md +3 -3
  35. package/{standards → claude/skills/docs-sync/references}/docs.md +11 -0
  36. package/{standards → claude/skills/docs-sync/references}/readme.md +9 -2
  37. package/claude/skills/draft-and-pick/SKILL.md +5 -5
  38. package/claude/skills/draft-doc/SKILL.md +6 -6
  39. package/claude/skills/draft-figure/REQUIREMENT.md +2 -2
  40. package/claude/skills/draft-figure/SKILL.md +2 -2
  41. package/claude/skills/draft-figure/references/hand-drawn.md +1 -1
  42. package/claude/skills/draft-figure/references/mermaid-path.md +1 -1
  43. package/claude/skills/draft-identity/REQUIREMENT.md +2 -2
  44. package/claude/skills/draft-identity/SKILL.md +2 -2
  45. package/claude/skills/draft-ready/SKILL.md +1 -1
  46. package/claude/skills/git-branch/SKILL.md +1 -1
  47. package/{standards → claude/skills/git-branch/references}/branch.md +1 -1
  48. package/claude/skills/git-commit/SKILL.md +2 -2
  49. package/claude/skills/git-followup/REQUIREMENT.md +1 -1
  50. package/claude/skills/git-followup/SKILL.md +1 -1
  51. package/claude/skills/git-issue/SKILL.md +1 -1
  52. package/claude/skills/git-issue/references/issue.md +4 -1
  53. package/claude/skills/git-pr/SKILL.md +13 -13
  54. package/claude/skills/git-pr/references/evidence.md +2 -2
  55. package/claude/skills/git-pr/references/final-command.md +38 -14
  56. package/claude/skills/git-pr/references/labels.md +5 -1
  57. package/{standards → claude/skills/git-pr/references}/pr.md +19 -9
  58. package/{standards → claude/skills/git-pr/references}/publish.md +4 -2
  59. package/claude/skills/git-pr/references/task-number.md +1 -1
  60. package/claude/skills/git-ship/SKILL.md +15 -14
  61. package/claude/skills/git-split/SKILL.md +3 -3
  62. package/claude/skills/git-stage/SKILL.md +1 -1
  63. package/claude/skills/plan-feature/REQUIREMENT.md +1 -1
  64. package/claude/skills/review-address/SKILL.md +1 -1
  65. package/claude/skills/review-address/references/post-review-findings.md +1 -1
  66. package/claude/skills/review-craft/references/ui.md +1 -1
  67. package/claude/skills/review-pr/REQUIREMENT.md +1 -1
  68. package/claude/skills/review-pr/SKILL.md +3 -3
  69. package/claude/skills/review-pr/references/body-variants.md +2 -2
  70. package/claude/skills/review-pr/references/key-changes.md +3 -3
  71. package/claude/skills/review-pr/references/post.md +4 -2
  72. package/claude/skills/review-ui/REQUIREMENT.md +1 -1
  73. package/claude/skills/review-ui/SKILL.md +8 -6
  74. package/claude/skills/review-ui/references/driving.md +8 -8
  75. package/claude/skills/review-ui/references/post.md +53 -17
  76. package/claude/skills/role-orchestrator/REQUIREMENT.md +3 -0
  77. package/claude/skills/role-orchestrator/SKILL.md +2 -2
  78. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +2 -2
  79. package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +8 -5
  80. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +32 -1
  81. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +2 -2
  82. package/claude/skills/role-orchestrator/references/orchestrator-resume.md +3 -0
  83. package/claude/skills/role-planner/REQUIREMENT.md +2 -1
  84. package/claude/skills/role-planner/SKILL.md +13 -2
  85. package/claude/skills/role-reviewer/SKILL.md +1 -1
  86. package/claude/skills/role-worker/REQUIREMENT.md +1 -0
  87. package/claude/skills/role-worker/SKILL.md +9 -1
  88. package/claude/skills/session-map/SKILL.md +1 -1
  89. package/claude/skills/session-resume/REQUIREMENT.md +2 -2
  90. package/claude/skills/session-worktree/SKILL.md +5 -5
  91. package/claude/skills/target-setup/SKILL.md +10 -4
  92. package/claude/skills/target-setup/references/detect.md +20 -3
  93. package/claude/skills/task-board/REQUIREMENT.md +7 -12
  94. package/claude/skills/task-board/SKILL.md +5 -17
  95. package/claude/skills/task-board/references/tasks.md +2 -2
  96. package/claude/skills/task-complete/REQUIREMENT.md +47 -0
  97. package/claude/skills/task-complete/SKILL.md +86 -0
  98. package/claude/skills/{task-board → task-complete}/references/archive.md +4 -4
  99. package/claude/skills/{context-fold → task-complete}/references/receipt-sweep.md +5 -5
  100. package/claude/skills/task-complete/references/unusable-baseline.md +19 -0
  101. package/claude/skills/teach-workspace/REQUIREMENT.md +6 -2
  102. package/claude/skills/teach-workspace/SKILL.md +8 -4
  103. package/claude/skills/teach-workspace/references/lesson-craft.md +1 -1
  104. package/claude/skills/{context-fold/references/promoted-pages.md → teach-workspace/references/promotion-fold.md} +3 -3
  105. package/claude/skills/teach-workspace/references/promotion.md +3 -3
  106. package/claude/skills/test-craft/REQUIREMENT.md +2 -3
  107. package/claude/skills/test-craft/SKILL.md +15 -3
  108. package/claude/skills/ui-checklist/SKILL.md +1 -1
  109. package/claude/skills/ux-audit/SKILL.md +1 -1
  110. package/claude/skills/ux-measure/SKILL.md +1 -1
  111. package/docs/agents/canvas.md +6 -2
  112. package/docs/agents/commands.md +4 -3
  113. package/docs/agents/context-audit-checks.md +4 -4
  114. package/docs/agents/gov-detect.md +47 -0
  115. package/docs/agents/index.md +7 -4
  116. package/docs/agents/indexes.md +1 -1
  117. package/docs/agents/install-and-sync.md +10 -11
  118. package/docs/agents/key-changes.md +5 -5
  119. package/docs/agents/label-coverage.md +11 -0
  120. package/docs/agents/migrate.md +1 -1
  121. package/docs/agents/pr-evidence-layout.md +70 -0
  122. package/docs/agents/pr-evidence.md +8 -9
  123. package/docs/agents/pr-frames.md +7 -2
  124. package/docs/agents/pr-lift.md +132 -0
  125. package/docs/agents/records-push.md +2 -2
  126. package/docs/agents/records-stale.md +12 -47
  127. package/docs/agents/records-validate.md +3 -7
  128. package/docs/agents/records.md +5 -27
  129. package/docs/agents/rule-citations.md +1 -1
  130. package/docs/agents/sandbox.md +4 -4
  131. package/docs/agents/scripting.md +2 -1
  132. package/docs/agents/skills-audit.md +1 -1
  133. package/docs/agents/tasks-plans.md +2 -2
  134. package/docs/agents/tasks.md +2 -2
  135. package/docs/target/migrations.md +10 -2
  136. package/docs/target/projects.md +4 -4
  137. package/docs/target/sync.md +1 -1
  138. package/docs/workflow/operating-model.md +2 -2
  139. package/docs/workflow/parallel-features.md +7 -7
  140. package/docs/workflow/skill-map-on-request.md +9 -9
  141. package/docs/workflow/skill-map.md +12 -11
  142. package/docs/workflow/visual-design-workflow.md +2 -2
  143. package/governance/detect.toml +201 -0
  144. package/governance/renames.toml +0 -11
  145. package/governance/rules/canon/context.md +13 -0
  146. package/governance/rules/claude/skill.md +17 -0
  147. package/governance/rules/code/000-code.md +1 -13
  148. package/governance/rules/code/010-testing.md +2 -30
  149. package/governance/rules/ui/400-ui.md +1 -1
  150. package/governance/rules/ui/480-canvas.md +13 -0
  151. package/governance/stacks/base.toml +2 -4
  152. package/package.json +4 -4
  153. package/scripts/core/install-check.sh +3 -3
  154. package/scripts/core/regen-hero.ts +0 -6
  155. package/scripts/lib/ui.sh +0 -168
  156. package/src/audits/catalog.ts +0 -1
  157. package/src/canvas/arrange.ts +66 -0
  158. package/src/canvas/capture.ts +20 -6
  159. package/src/canvas/content.ts +39 -0
  160. package/src/canvas/fit.ts +238 -0
  161. package/src/canvas/tokens.ts +28 -10
  162. package/src/capture/frame-values.ts +2 -32
  163. package/src/claude/cases/misc.ts +1 -1
  164. package/src/claude/cases/setup.ts +4 -0
  165. package/src/claude/cases/workflow.ts +6 -1
  166. package/src/claude/seeds.ts +1 -1
  167. package/src/claude/skills-audit.ts +2 -2
  168. package/src/cli/help.ts +3 -1
  169. package/src/cli/ui.ts +2 -3
  170. package/src/commands/canvas.ts +179 -1
  171. package/src/commands/claude.ts +1 -1
  172. package/src/commands/gov.ts +146 -16
  173. package/src/commands/labels.ts +326 -7
  174. package/src/commands/migrate.ts +6 -1
  175. package/src/commands/pr.ts +377 -2
  176. package/src/commands/records.ts +13 -399
  177. package/src/commands/sandbox.ts +3 -6
  178. package/src/commands/tasks.ts +3 -3
  179. package/src/context/audit.ts +5 -5
  180. package/src/context/folders.ts +1 -1
  181. package/src/design/budget.ts +1 -1
  182. package/src/design/components.ts +1 -1
  183. package/src/design/document.ts +1 -1
  184. package/src/design/regen.ts +1 -1
  185. package/src/design/render.ts +1 -1
  186. package/src/feedback/body.ts +26 -5
  187. package/src/gate/measures.ts +59 -45
  188. package/src/gate/sequencer.ts +56 -1
  189. package/src/gate/stages.ts +4 -11
  190. package/src/gov/detect.ts +301 -0
  191. package/src/gov/install.ts +2 -3
  192. package/src/gov/list.ts +23 -2
  193. package/src/gov/stacks.ts +10 -1
  194. package/src/hooks/post-merge.ts +1 -1
  195. package/src/labels/apply.ts +39 -0
  196. package/src/labels/format.ts +3 -3
  197. package/src/labels/phase.ts +3 -3
  198. package/src/labels/sections.ts +43 -0
  199. package/src/migrate/skill-names.ts +20 -1
  200. package/src/pr/evidence.ts +218 -45
  201. package/src/pr/frames.ts +23 -3
  202. package/src/pr/lift.ts +418 -0
  203. package/src/pr/paths.ts +4 -4
  204. package/src/pr/rest.ts +3 -0
  205. package/src/pr/review-scope.ts +76 -0
  206. package/src/records/backup.ts +16 -6
  207. package/src/records/stale-canonical.ts +31 -1
  208. package/src/records/validate.ts +2 -188
  209. package/src/sandbox/census.ts +1 -1
  210. package/src/sandbox/coverage.ts +2 -6
  211. package/src/sandbox/equivalence.ts +19 -51
  212. package/src/sandbox/expect.ts +4 -4
  213. package/src/sandbox/provision.ts +13 -36
  214. package/src/sandbox/scenario.ts +26 -48
  215. package/src/sandbox/tree.ts +2 -6
  216. package/src/standards/read.ts +14 -1
  217. package/src/sync/git.ts +1 -1
  218. package/src/tasks/archive.ts +5 -5
  219. package/src/tasks/branch.ts +3 -3
  220. package/src/tasks/label.ts +1 -1
  221. package/src/tasks/record.ts +2 -2
  222. package/src/tasks/validate.ts +1 -1
  223. package/standards/index.md +0 -13
  224. package/standards/standard.md +1 -7
  225. package/tooling/base/reference.md +1 -1
  226. package/tooling/base/seeds/.github/pull_request_template.md +2 -2
  227. package/tooling/claude/reference.md +1 -1
  228. package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +9 -1
  229. package/tooling/claude/seeds/.claude/hooks/review-post-scan.sh +171 -0
  230. package/tooling/claude/seeds/.claude/settings.json +9 -0
  231. package/governance/rules/standards/context.md +0 -14
  232. package/governance/rules/standards/design.md +0 -12
  233. package/governance/rules/standards/readme.md +0 -12
  234. package/governance/rules/standards/rule.md +0 -13
  235. package/governance/rules/standards/skill.md +0 -21
  236. package/governance/rules/standards/standard.md +0 -13
  237. package/scripts/config.sh +0 -11
  238. package/scripts/lib/gov.sh +0 -14
  239. package/scripts/lib/sandbox-fixtures.sh +0 -191
  240. package/scripts/lib/sandbox-git.sh +0 -125
  241. package/scripts/lib/sandbox-path.sh +0 -50
  242. package/scripts/sandbox-hook.sh +0 -99
  243. package/src/gov/standard-rules.ts +0 -179
  244. package/src/records/migrate.ts +0 -78
  245. package/src/records/stale.ts +0 -243
  246. package/src/sandbox/hooks.ts +0 -128
  247. /package/{standards → claude/skills/draft-figure/references}/figures.md +0 -0
  248. /package/{standards → claude/skills/git-branch/references}/slug.md +0 -0
  249. /package/{standards → claude/skills/git-commit/references}/commit.md +0 -0
  250. /package/{standards → claude/skills/git-commit/references}/versioning.md +0 -0
@@ -42,15 +42,13 @@ export const FIXTURE_SUFFIX = '.fixture'
42
42
  /** A scenario whose only arm carries this name routes none, as a bash file with no picker did. */
43
43
  export const DEFAULT_ARM_NAME = 'default'
44
44
 
45
- /** What a bash scenario passed to `select_or_route_scenario` unless it named another. */
45
+ /** The picker's message unless a scenario names another. */
46
46
  const ROUTE_PROMPT = 'Which scenario?'
47
47
 
48
48
  /** Every scenario that sets the anchor pushes to this one throwaway repository. */
49
49
  export const SANDBOX_ANCHOR_REPO = 'canon-sandbox'
50
50
 
51
- const EXTENSIONS = ['.ts', '.sh'] as const
52
-
53
- export type ScenarioKind = 'ts' | 'sh'
51
+ const EXTENSION = '.ts'
54
52
 
55
53
  export type ArmSetup = (ctx: StageContext) => void | Promise<void>
56
54
 
@@ -60,12 +58,12 @@ export interface ScenarioDefinition {
60
58
  /** Stages the tree from the anchor fixture and names the throwaway remote. */
61
59
  readonly anchor?: boolean
62
60
  /**
63
- * Stages and narrates what the bash form did ahead of `select_or_route_scenario`,
64
- * so the picker's echo lands after it on the frame. Runs before the arm is
61
+ * Stages and narrates ahead of the picker, so the picker's echo lands after
62
+ * it on the frame. Runs before the arm is
65
63
  * routed, which leaves `ctx.arm` undefined.
66
64
  */
67
65
  readonly prepare?: ArmSetup
68
- /** The picker's message, for a bash scenario that passed something other than the default. */
66
+ /** The picker's message, for a scenario that asks something other than the default. */
69
67
  readonly prompt?: string
70
68
  /** In routing order, so the first arm is the one a headless caller gets. */
71
69
  readonly arms: Readonly<Record<string, ArmSetup>>
@@ -78,43 +76,27 @@ export function scenario(definition: ScenarioDefinition): ScenarioDefinition {
78
76
  export interface ScenarioFile {
79
77
  readonly command: string
80
78
  readonly file: string
81
- readonly kind: ScenarioKind
82
79
  }
83
80
 
84
81
  /**
85
- * Lists the scenarios in one category folder, one per stem. A stem present in
86
- * both forms is reported apart rather than listed twice, since picking one
87
- * without saying so is the failure the refusal exists to prevent.
82
+ * Lists the scenarios in one category folder, one per `.ts` stem. A file in any
83
+ * other form is ignored rather than refused, so a hand-written leftover does not
84
+ * break the listing.
88
85
  */
89
86
  export function listScenarioFiles(categoryDir: string): {
90
87
  scenarios: ScenarioFile[]
91
- ambiguous: string[]
92
88
  } {
93
- const byStem = new Map<string, ScenarioFile[]>()
89
+ const scenarios: ScenarioFile[] = []
94
90
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
95
- if (!entry.isFile()) continue
96
- const kind = EXTENSIONS.find((ext) => entry.name.endsWith(ext))
97
- if (kind === undefined) continue
98
- const command = entry.name.slice(0, -kind.length)
99
- const found = byStem.get(command) ?? []
100
- found.push({
101
- command,
91
+ if (!entry.isFile() || !entry.name.endsWith(EXTENSION)) continue
92
+ scenarios.push({
93
+ command: entry.name.slice(0, -EXTENSION.length),
102
94
  file: join(categoryDir, entry.name),
103
- kind: kind === '.ts' ? 'ts' : 'sh',
104
95
  })
105
- byStem.set(command, found)
106
96
  }
97
+ scenarios.sort((a, b) => a.command.localeCompare(b.command))
107
98
 
108
- const scenarios: ScenarioFile[] = []
109
- const ambiguous: string[] = []
110
- for (const [command, found] of [...byStem].sort(([a], [b]) =>
111
- a.localeCompare(b),
112
- )) {
113
- if (found.length > 1) ambiguous.push(command)
114
- else if (found[0] !== undefined) scenarios.push(found[0])
115
- }
116
-
117
- return { scenarios, ambiguous }
99
+ return { scenarios }
118
100
  }
119
101
 
120
102
  /** Every category folder under `sandbox/`, fixtures excluded. */
@@ -129,21 +111,18 @@ export function listCategories(sandboxDir: string): string[] {
129
111
 
130
112
  export type ResolvedScenario =
131
113
  | ({ readonly ok: true } & ScenarioFile)
132
- | { readonly ok: false; readonly reason: 'missing' | 'ambiguous' }
114
+ | { readonly ok: false; readonly reason: 'missing' }
133
115
 
134
116
  export function resolveScenarioFile(
135
117
  sandboxDir: string,
136
118
  category: string,
137
119
  command: string,
138
120
  ): ResolvedScenario {
139
- const found = EXTENSIONS.map((ext) =>
140
- join(sandboxDir, category, `${command}${ext}`),
141
- ).filter((path) => existsSync(path) && statSync(path).isFile())
142
- if (found.length > 1) return { ok: false, reason: 'ambiguous' }
143
- const file = found[0]
144
- if (file === undefined) return { ok: false, reason: 'missing' }
145
-
146
- return { ok: true, command, file, kind: file.endsWith('.ts') ? 'ts' : 'sh' }
121
+ const file = join(sandboxDir, category, `${command}${EXTENSION}`)
122
+ if (!existsSync(file) || !statSync(file).isFile())
123
+ return { ok: false, reason: 'missing' }
124
+
125
+ return { ok: true, command, file }
147
126
  }
148
127
 
149
128
  function isRecord(value: unknown): value is Record<string, unknown> {
@@ -190,7 +169,7 @@ export function armNames(definition: ScenarioDefinition): string[] {
190
169
  return names.length === 1 && names[0] === DEFAULT_ARM_NAME ? [] : names
191
170
  }
192
171
 
193
- /** Twin of `scripts/config.sh`, which derives the organization from the toolkit's own remote. */
172
+ /** Derives the organization from the toolkit's own remote unless `GITHUB_ORG` names one. */
194
173
  function deriveGithubOrg(root: string, env: NodeJS.ProcessEnv): string {
195
174
  if ((env.GITHUB_ORG ?? '') !== '') return env.GITHUB_ORG ?? ''
196
175
 
@@ -209,8 +188,8 @@ function deriveGithubOrg(root: string, env: NodeJS.ProcessEnv): string {
209
188
  }
210
189
 
211
190
  /**
212
- * What the bash probe reported for a scenario: its config, the organization
213
- * `config.sh` derives, and the anchor's repository name. These land in the one
191
+ * What provisioning reads off a scenario: its config, the derived organization,
192
+ * and the anchor's repository name. These land in the one
214
193
  * environment provisioning passes to every child, the seed and gov installs
215
194
  * included.
216
195
  */
@@ -447,8 +426,7 @@ export function createStageContext(options: ContextOptions): StageContext {
447
426
 
448
427
  /**
449
428
  * `gh` answers an absent repository and an unreachable host with one status,
450
- * so the 404 alone separates them. Creating is the opt-in, for the reason
451
- * `ensure_sandbox_anchor_repo` in `scripts/lib/sandbox-git.sh` records.
429
+ * so the 404 alone separates them. Creating is the opt-in.
452
430
  */
453
431
  const ensureAnchorRepo = (org: string, repo: string): void => {
454
432
  const probe = spawn(
@@ -503,7 +481,7 @@ export function createStageContext(options: ContextOptions): StageContext {
503
481
  git('remote', 'add', 'origin', `https://github.com/${org}/${repo}.git`)
504
482
  }
505
483
 
506
- /** Twin of `stage_fixtures` in `scripts/lib/sandbox-fixtures.sh`. */
484
+ /** Copies a fixture folder into the tree. */
507
485
  const fixtures = (
508
486
  category: string,
509
487
  command: string,
@@ -602,7 +580,7 @@ export function createStageContext(options: ContextOptions): StageContext {
602
580
  }
603
581
 
604
582
  /**
605
- * Picks the arm the way `select_or_route_scenario` did: a routed name first,
583
+ * Picks the arm: a routed name first,
606
584
  * echoed on the frame, then the first arm for a headless caller, then a prompt.
607
585
  */
608
586
  async function routeArm(
@@ -8,11 +8,9 @@ import { join } from 'node:path'
8
8
  * in `CANON_SANDBOX_RUN_ID` for the rest of this process, so a script that
9
9
  * spawns a child inheriting `process.env` — `canon sandbox run` calling
10
10
  * `canon sandbox` to provision and then `canon sandbox check` — resolves the same tree in every one
11
- * of them, as do the hook and installer children provisioning spawns. A process
11
+ * of them, as do the installer children provisioning spawns. A process
12
12
  * that already carries the variable, inherited from such a parent, reuses it
13
13
  * rather than minting a new one.
14
- *
15
- * Twin of `mint_sandbox_run_id` in `scripts/lib/sandbox-path.sh`.
16
14
  */
17
15
  export function mintSandboxRunId(): string {
18
16
  const existing = process.env.CANON_SANDBOX_RUN_ID
@@ -28,9 +26,7 @@ export function mintSandboxRunId(): string {
28
26
  * per-run default is unit-testable on its own rather than only through the
29
27
  * command's registration.
30
28
  *
31
- * Twin of `resolve_sandbox_dir` in `scripts/lib/sandbox-path.sh`. The exec
32
- * boundary rules out a shared constant, so a change to the default lands on
33
- * both sides. The fall-through used to be one path per machine, so two
29
+ * The fall-through used to be one path per machine, so two
34
30
  * sessions each resolving the default at once provisioned over each other
35
31
  * with neither told; `mintSandboxRunId` gives the path a per-run component
36
32
  * instead, which is what makes two such sessions land on two different trees.
@@ -60,8 +60,21 @@ export function standardRoots(root: string): StandardRoot[] {
60
60
  */
61
61
  const MOVED_STANDARDS: Readonly<Record<string, string>> = {
62
62
  board: 'claude/skills/task-board/references/board.md',
63
+ context: 'claude/skills/context-fold/references/context.md',
64
+ design: 'claude/skills/canvas-board/references/design.md',
65
+ docs: 'claude/skills/docs-sync/references/docs.md',
66
+ figures: 'claude/skills/draft-figure/references/figures.md',
67
+ readme: 'claude/skills/docs-sync/references/readme.md',
68
+ rule: 'claude/skills/create-rule/references/rule.md',
69
+ skill: 'claude/skills/create-skill/references/skill.md',
70
+ branch: 'claude/skills/git-branch/references/branch.md',
71
+ commit: 'claude/skills/git-commit/references/commit.md',
72
+ pr: 'claude/skills/git-pr/references/pr.md',
73
+ publish: 'claude/skills/git-pr/references/publish.md',
74
+ slug: 'claude/skills/git-branch/references/slug.md',
75
+ versioning: 'claude/skills/git-commit/references/versioning.md',
63
76
  markdown: 'claude/skills/markdown-craft/references/markdown.md',
64
- canvas: 'claude/skills/canvas/references/canvas.md',
77
+ canvas: 'claude/skills/canvas-board/references/canvas.md',
65
78
  glossary: 'claude/skills/teach-workspace/references/glossary.md',
66
79
  groundwork: 'claude/skills/plan-groundwork/references/groundwork.md',
67
80
  'groundwork-spikes':
package/src/sync/git.ts CHANGED
@@ -96,7 +96,7 @@ export function pullRequestBody(changes: readonly DomainChange[]): string {
96
96
  '',
97
97
  `Sync ${domains} from toolkit.`,
98
98
  '',
99
- '## Key Changes',
99
+ '## Key changes',
100
100
  '',
101
101
  ]
102
102
 
@@ -578,7 +578,7 @@ function isBulletFor(line: string, target: string): boolean {
578
578
 
579
579
  /**
580
580
  * Resolves the `Plan:` target against the board and against the project root
581
- * both, which is how `context-fold` reads the same line. It accepts `../plans/x.md`
581
+ * both, which is how `task-complete` reads the same line. It accepts `../plans/x.md`
582
582
  * and `.canon/plans/x.md` as one file, so a gate reading only the first form
583
583
  * would pass the second and strand the plan this exists to protect.
584
584
  *
@@ -633,7 +633,7 @@ function atOneRoot(path: string, plans: string[], root: string): string {
633
633
  /**
634
634
  * Names the other live tasks whose `Plan:` line lands on the same file through
635
635
  * any of its links. This is
636
- * the rule `context-fold` applies before it archives a plan, held here so one
636
+ * the rule the `post-merge` hook applies before it archives a plan, held here so one
637
637
  * question has one implementation: a plan another live task still cites is a
638
638
  * plan the sweep is correct to leave, and a guard that read the folder instead
639
639
  * refused every task sharing one plan and deadlocked the board against the
@@ -697,7 +697,7 @@ export type CitationOutcome = PlanCitations | ArchiveRefused
697
697
 
698
698
  /**
699
699
  * Answers where one task's plan sits and who else holds it, which is the whole
700
- * of the last-live-citation rule. `context-fold` reads this rather than scanning
700
+ * of the last-live-citation rule. The archive reads this rather than scanning
701
701
  * the board itself, so the sweep that moves a plan and the gate that refuses a
702
702
  * task archive cannot drift into disagreeing about which plan is free.
703
703
  *
@@ -897,7 +897,7 @@ async function resolveStem(
897
897
 
898
898
  /**
899
899
  * Archives one task as a single unit: the move, the ordering-row removal, and
900
- * the index regen. The hook and `task-board` both call this, so every gate
900
+ * the index regen. The hook and `task-complete` both call this, so every gate
901
901
  * refuses rather than reports. A caller with nobody watching cannot act on a
902
902
  * warning, and two callers gating differently is the drift this exists to stop.
903
903
  */
@@ -918,7 +918,7 @@ export async function archiveTask(
918
918
  const from = join(dir, `${stem}.md`)
919
919
  const text = await readFile(from, 'utf8')
920
920
 
921
- // `context-fold` ticks outcomes at ship time, before the merge, so an earlier
921
+ // `task-complete` ticks outcomes at ship time, before the merge, so an earlier
922
922
  // slice merging after the last slice shipped would find every box ticked
923
923
  // while the last slice is still open. Only the last number closes the task.
924
924
  const last = readPullRequest(text).at(-1)
@@ -14,10 +14,10 @@ const MARKDOWN = '.md'
14
14
  */
15
15
  export const PLAN_BRANCH_TYPE = 'feat'
16
16
 
17
- /** The description cap in `standards/branch.md`, in kebab-separated words. */
17
+ /** The description cap in `claude/skills/git-branch/references/branch.md`, in kebab-separated words. */
18
18
  export const DESCRIPTION_WORD_CAP = 4
19
19
 
20
- /** The branch length cap in `standards/branch.md`, in characters. */
20
+ /** The branch length cap in `claude/skills/git-branch/references/branch.md`, in characters. */
21
21
  export const BRANCH_LENGTH_CAP = 50
22
22
 
23
23
  export interface PlanBranch {
@@ -48,7 +48,7 @@ function slugOf(path: string): string {
48
48
  * of them name. It is the one derivation, so the collision check and the
49
49
  * worktree entry it gates cannot hold two answers for one plan.
50
50
  *
51
- * Conformance covers both caps `standards/branch.md` states, being the word
51
+ * Conformance covers both caps `claude/skills/git-branch/references/branch.md` states, being the word
52
52
  * count of the description and the length of the whole branch. A slug is a
53
53
  * plan's own filename rather than a name anyone chose for a branch, so a plan
54
54
  * can name a branch this refuses to grade as conforming, and reporting that is
@@ -185,7 +185,7 @@ async function reserveLabel(root: string, label: string): Promise<boolean> {
185
185
  * the task file after this returns, so a reservation with no file behind it is
186
186
  * the ordinary state for a few seconds, and a scan blind to it would offer the
187
187
  * same label to every retry. Reservations are never expired: a claim that died
188
- * leaves a gap, and `standards/versioning.md` permits gaps.
188
+ * leaves a gap, and `claude/skills/git-commit/references/versioning.md` permits gaps.
189
189
  */
190
190
  export async function claimLabel(
191
191
  root: string,
@@ -420,7 +420,7 @@ async function openTask(
420
420
  * resolve the board root in-process.
421
421
  *
422
422
  * `branches` names the recording branch and every name it carried before a
423
- * rename, since `git-ship` renames a branch after `context-fold` has already
423
+ * rename, since `git-ship` renames a branch after `task-complete` has already
424
424
  * written the marker under the old name. Each comes off the `Pending branch:`
425
425
  * line even when the number was already listed, so a rerun repairs a stale
426
426
  * marker.
@@ -504,7 +504,7 @@ export async function recordPlan(
504
504
  }
505
505
 
506
506
  /**
507
- * Marks the named outcomes `[x]` in place. `context-fold` runs this against a
507
+ * Marks the named outcomes `[x]` in place. `task-complete` runs this against a
508
508
  * board it can read and cannot edit from a linked worktree, and the positions
509
509
  * come from the read it already made.
510
510
  *
@@ -597,7 +597,7 @@ async function listTaskStems(dir: string): Promise<string[]> {
597
597
 
598
598
  /**
599
599
  * Resolves a pointer against the board and against the project root both, the
600
- * way `context-fold` reads the same line. A fragment is dropped first, since an
600
+ * way `task-complete` reads the same line. A fragment is dropped first, since an
601
601
  * anchor is part of the link and never part of the path.
602
602
  */
603
603
  function resolves(target: string, dir: string, root: string): boolean {
@@ -7,17 +7,4 @@ subtitle: Reference docs for consistent authoring across the toolkit and target
7
7
 
8
8
  Reference docs for consistent authoring across the toolkit and target projects.
9
9
 
10
- - [Branch reference](branch.md): Branch naming format and type conventions
11
- - [Commit reference](commit.md): Commit message format and type conventions
12
- - [Context entry reference](context.md): Shape and content rules for canon/context/<domain>.md entries
13
- - [Design reference](design.md): Shape and content rules for canon/DESIGN.md
14
- - [Docs reference](docs.md): Reader and jurisdiction, frontmatter, page structure, what a page links out to, the diagram permission, and when a category earns a subfolder
15
- - [Figure reference](figures.md): When a figure earns its place, the render-first policy for a graph-shaped subject, freehand SVG as the escape hatch, and its wrapping, color, and accessibility rules
16
- - [Pull request reference](pr.md): Pull request title and body conventions
17
- - [Publish reference](publish.md): Scan an author runs against finished text, the cross-reference form each destination takes, and the response to an unreadable source
18
- - [Readme reference](readme.md): Readme voice, structure, and content conventions
19
- - [Governance rule reference](rule.md): Rule frontmatter, body shape, and voice for .claude/rules files
20
- - [Claude skill reference](skill.md): Skill structure and authoring rules for Claude Code
21
- - [Slug reference](slug.md): Transform from a git branch name to a slug, and the three responses to an empty result
22
10
  - [Standard reference](standard.md): Shape and content rules for authoring a standard
23
- - [Versioning reference](versioning.md): Phase label vs semver discipline across tasks, PRs, reviews, issues, commits, and tags
@@ -1,10 +1,6 @@
1
1
  ---
2
2
  title: Standard reference
3
3
  description: Shape and content rules for authoring a standard
4
- paths:
5
- - 'standards/**/*.md'
6
- rule:
7
- - 'Read it before writing or editing a standard. Do not work the shape from memory.'
8
4
  ---
9
5
 
10
6
  # Standard reference
@@ -68,9 +64,7 @@ A standard failing these questions is non-conforming even when it satisfies ever
68
64
 
69
65
  - Start the file with a frontmatter block carrying at least `title` and `description`. A consuming surface may require more.
70
66
  - `title`: names the doc type in sentence case, suffixed `reference` (`Commit reference`, `Branch reference`)
71
- - `description`: one line naming what the standard covers. It becomes the index link label on install, and the generated rule's description and pointer bullet.
72
- - `paths` (optional): the globs where the standard applies, one per entry, copied verbatim into the rule `canon gov regen` generates for it. Omit it when no path should route to the standard, and no rule is generated.
73
- - `rule` (optional): operative bullets the generated rule carries after its pointer bullet, one per entry, each a full sentence. Quote an entry holding a backtick, a colon, or a leading `-`.
67
+ - `description`: one line naming what the standard covers. It becomes the index link label on install.
74
68
 
75
69
  ## Structure
76
70
 
@@ -29,7 +29,7 @@ Seeds live in `tooling/base/seeds/`. Sync drops each once on first install and n
29
29
  - `.lintstagedrc`: the glob map below.
30
30
  - `.prettierignore`: created empty. Projects add their own entries.
31
31
  - `.github/workflows/verify.yml`: runs on pull requests targeting `main` and on `workflow_dispatch`. A project with more than one language root adds its own jobs here.
32
- - `.github/pull_request_template.md`: `## Summary`, `## Key Changes`, `## Technical Context`, `## Testing`. A project adds the checklist lines its own review needs.
32
+ - `.github/pull_request_template.md`: `## Summary`, `## Key changes`, `## Technical context`, `## Testing`. A project adds the checklist lines its own review needs.
33
33
  - `canon/context/`: extend the `ci` and `development` entries with project-specific commands, workflows, or deploy steps. Canonical rationale stays in this reference.
34
34
 
35
35
  ## Tool pairing
@@ -2,12 +2,12 @@
2
2
 
3
3
  <Action Verb> <Direct Object> to <Result>.
4
4
 
5
- ## Key Changes
5
+ ## Key changes
6
6
 
7
7
  - <Verb> <specific component/file/function> (<reason if non-obvious>)
8
8
  - <Verb> <specific component/file/function>
9
9
 
10
- <!-- ## Technical Context (optional, omit for docs/config/trivial changes) -->
10
+ <!-- ## Technical context (optional, omit for docs/config/trivial changes) -->
11
11
  <!-- - <Why this approach was taken, not what changed> -->
12
12
 
13
13
  ## Testing
@@ -21,7 +21,7 @@ canon/
21
21
 
22
22
  .canon/
23
23
  ├── tasks/ ← seeded. One file per task plus a generated index.md, local scratch only. `archive/` holds the tasks that shipped.
24
- ├── plans/ ← execution detail for multi-step tasks. `feature-*.md` entries swept by context-fold into `archive/`.
24
+ ├── plans/ ← execution detail for multi-step tasks. `feature-*.md` entries moved into `archive/` by `canon tasks archive` when the `post-merge` hook archives their task.
25
25
  ├── review/ ← one subfolder per producer. `branch/` no longer written, `design/` for canon design render.
26
26
  └── tmp/ ← ephemeral scratch space, safe to delete without loss
27
27
  ```
@@ -24,8 +24,9 @@ IFS= read -r -d '' -t 2 input
24
24
  # its edit path prints a URL as well and is not an opening.
25
25
  command=$(printf '%s' "$input" | jq -r '.tool_input.command // empty')
26
26
  stdout=$(printf '%s' "$input" | jq -r '.tool_response.stdout // empty')
27
+ is_hand_opened=false
27
28
  case "$command" in
28
- *"gh pr create"*) ;;
29
+ *"gh pr create"*) is_hand_opened=true ;;
29
30
  *)
30
31
  printf '%s\n' "$stdout" | grep -qx 'opened=true' || exit 0
31
32
  ;;
@@ -57,4 +58,11 @@ session=$(printf '%s' "$input" | jq -r '.session_id // "unknown"')
57
58
  printf -- '- %s session=%s pr=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$session" "$url" >>"$log_dir/log.md"
58
59
 
59
60
  msg="Pull request $url just opened. If this session holds a worker's channel obligation, announce it to the controller now, per role-worker."
61
+ # A pull request opened by hand skipped git-pr, so nothing has labelled it.
62
+ # The verb is named only where the installed canon carries it, since a skill
63
+ # can reach a target ahead of the release that ships the verb.
64
+ if [ "$is_hand_opened" = true ] && canon labels apply --help >/dev/null 2>&1; then
65
+ msg="$msg Run canon labels apply ${url##*/} to label it."
66
+ fi
67
+
60
68
  jq -nc --arg msg "$msg" '{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:$msg}}'
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Claude Code sends a payload and closes stdin. A bare read with nothing feeding
4
+ # it blocks forever and holds the session open, so the read is bounded. `read`
5
+ # rather than `timeout cat`, which macOS does not ship.
6
+ IFS= read -r -d '' -t 2 input
7
+ [ -n "$input" ] || {
8
+ printf '%s reads a Claude Code hook payload on stdin and cannot be run by hand.\n' "${0##*/}" >&2
9
+ exit 1
10
+ }
11
+
12
+ # Every Bash call reaches this line, so the command is read in one jq pass and
13
+ # matched with a shell pattern before anything else costs a process.
14
+ fields=$(printf '%s' "$input" | jq -r 'if .tool_name == "Bash" then [(.cwd // ""), (.tool_input.command // "")] | @json else empty end')
15
+ [ -n "$fields" ] || exit 0
16
+
17
+ cmd=$(printf '%s' "$fields" | jq -r '.[1]')
18
+ case "$cmd" in
19
+ *'gh api'*) ;;
20
+ *) exit 0 ;;
21
+ esac
22
+
23
+ # A command that only mentions a post is not one: a heredoc writing a review
24
+ # body that quotes the command, or a search whose pattern holds it. The string
25
+ # is cut into the commands it runs, with heredoc bodies dropped and quoted text
26
+ # left whole, and only a command that starts with `gh api` is read further.
27
+ # Splitting on the separators outside quotes is what keeps `echo 'a; gh api'`
28
+ # one command rather than two.
29
+ IFS= read -r -d '' splitter <<'AWK'
30
+ { s = s (NR > 1 ? "\n" : "") $0 }
31
+ function emit() {
32
+ gsub(/\n/, " ", seg)
33
+ sub(/^[ \t]+/, "", seg)
34
+ if (seg != "") print seg
35
+ seg = ""
36
+ }
37
+ END {
38
+ n = length(s); q = ""; seg = ""; nt = 0; i = 1
39
+ while (i <= n) {
40
+ c = substr(s, i, 1)
41
+ if (q == "'") { seg = seg c; if (c == "'") q = ""; i++; continue }
42
+ if (q == "\"") {
43
+ if (c == "\\") { seg = seg c substr(s, i + 1, 1); i += 2; continue }
44
+ if (c == "\"") q = ""
45
+ seg = seg c; i++; continue
46
+ }
47
+ if (c == "\\") { seg = seg c substr(s, i + 1, 1); i += 2; continue }
48
+ if (c == "'" || c == "\"") { q = c; seg = seg c; i++; continue }
49
+ if (c == "#" && (seg == "" || substr(seg, length(seg), 1) ~ /[ \t]/)) {
50
+ while (i <= n && substr(s, i, 1) != "\n") i++
51
+ continue
52
+ }
53
+ if (c == "<" && substr(s, i, 2) == "<<" && substr(s, i, 3) != "<<<") {
54
+ j = i + 2; dash = 0
55
+ if (substr(s, j, 1) == "-") { dash = 1; j++ }
56
+ while (substr(s, j, 1) ~ /[ \t]/) j++
57
+ oq = substr(s, j, 1)
58
+ if (oq == "'" || oq == "\"") j++
59
+ tag = ""
60
+ while (substr(s, j, 1) ~ /[A-Za-z0-9_]/) { tag = tag substr(s, j, 1); j++ }
61
+ if (oq == "'" || oq == "\"") j++
62
+ if (tag != "") { nt++; tags[nt] = tag; dashes[nt] = dash; i = j; continue }
63
+ }
64
+ if (c == "\n") {
65
+ emit(); i++
66
+ for (t = 1; t <= nt; t++) {
67
+ while (i <= n) {
68
+ e = index(substr(s, i), "\n")
69
+ if (e == 0) { line = substr(s, i); i = n + 1 } else { line = substr(s, i, e - 1); i += e }
70
+ if (dashes[t]) sub(/^\t+/, "", line)
71
+ if (line == tags[t]) break
72
+ }
73
+ }
74
+ nt = 0; continue
75
+ }
76
+ if (c == ";" || c == "|" || c == "(" || c == ")" || c == "`" || c == "&") {
77
+ if (c == "&" && (substr(seg, length(seg), 1) ~ /[<>]/ || substr(s, i + 1, 1) == ">")) { seg = seg c; i++; continue }
78
+ emit(); i++; continue
79
+ }
80
+ seg = seg c; i++
81
+ }
82
+ emit()
83
+ }
84
+ AWK
85
+
86
+ # A block reaches the session through stderr, and stdout stays empty so nothing
87
+ # unframed lands in the hook protocol.
88
+ deny() {
89
+ printf '%s\n' "$1" >&2
90
+ exit 2
91
+ }
92
+
93
+ # The thread a review or a comment lands on: a review post or edit, and an issue
94
+ # or pull request comment post or edit. A pull request's inline comments are left
95
+ # out, since no shipped skill posts one.
96
+ seg="[^/[:space:]'\"]+"
97
+ end="(['\" ?]|\$)"
98
+ endpoint="(pulls/$seg/reviews(/$seg)?|issues/$seg/comments|issues/comments/$seg)$end"
99
+ starts_gh_api="^([A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*[[:space:]]+)*((command|exec)[[:space:]]+)?gh[[:space:]]+api([[:space:]]|\$)"
100
+ body_inline="[[:space:]](-f|-F|--field|--raw-field)[[:space:]=]*['\"]?body="
101
+ body_file="[[:space:]](-F|--field)[[:space:]=]*['\"]?body=@([^'\"[:space:]]+)"
102
+
103
+ # Reads one command already known to start with `gh api` and returns when it
104
+ # has nothing to scan or the scan came back clean.
105
+ check() {
106
+ local call=$1 method='' file='' cwd out code warnings
107
+
108
+ [[ $call =~ $endpoint ]] || return 0
109
+
110
+ # gh sends POST when a field flag is present and no method is named, and the
111
+ # explicit method wins when one is. A call naming neither is a read.
112
+ if [[ $call =~ (-X|--method)[[:space:]=]*([A-Za-z]+) ]]; then
113
+ method=$(printf '%s' "${BASH_REMATCH[2]}" | tr '[:lower:]' '[:upper:]')
114
+ elif [[ $call =~ [[:space:]](-f|-F|--field|--raw-field|--input)([[:space:]=]|$) ]]; then
115
+ method=POST
116
+ fi
117
+ case "$method" in
118
+ POST | PATCH | PUT) ;;
119
+ *) return 0 ;;
120
+ esac
121
+
122
+ if [[ $call =~ $body_file ]]; then
123
+ file=${BASH_REMATCH[2]}
124
+ elif [[ $call =~ $body_inline ]]; then
125
+ deny 'This review or comment post passes its body inline, so the label scan cannot read it. Write the body to a file in an earlier call and pass it as -F body=@<file>.'
126
+ else
127
+ return 0
128
+ fi
129
+
130
+ [ "$file" != '-' ] || deny 'This post reads its body from stdin, so the label scan cannot read it. Write the body to a file in an earlier call and pass it as -F body=@<file>.'
131
+
132
+ if ! command -v canon >/dev/null 2>&1; then
133
+ jq -nc '{hookSpecificOutput:{hookEventName:"PreToolUse",additionalContext:"The label scan did not run on this review or comment post, since canon is not on PATH. Run canon labels scan --body-file on the body once it is."}}'
134
+ exit 0
135
+ fi
136
+
137
+ # The payload's own cwd is the directory a relative path was written against.
138
+ # The project dir and this process's directory can each be a different worktree.
139
+ cwd=$(printf '%s' "$fields" | jq -r '.[0]')
140
+ [ -n "$cwd" ] || cwd=${CLAUDE_PROJECT_DIR:-.}
141
+ case "$file" in
142
+ /*) ;;
143
+ *) file="$cwd/$file" ;;
144
+ esac
145
+
146
+ # A body written by the same call does not exist when this hook runs, so the
147
+ # post would go out unread by the scan. Blocking is the only verdict that holds.
148
+ [ -f "$file" ] || deny "The body file $file does not exist yet, so the label scan cannot read it. Write the body in an earlier call, then post it."
149
+
150
+ # No --title: a review body carries none, and a title would grade it as an
151
+ # ordinary pull request body.
152
+ out=$(cd "$cwd" && canon labels scan --body-file "$file" 2>&1)
153
+ code=$?
154
+ [ "$code" -ne 0 ] || return 0
155
+
156
+ warnings=$(printf '%s\n' "$out" | grep -F '!')
157
+ [ -n "$warnings" ] || warnings=$out
158
+ if [ "$code" -eq 2 ]; then
159
+ deny "The label scan found text this post would publish. Fix the body file and post again.
160
+ $warnings"
161
+ fi
162
+ deny "The label scan could not grade $file (exit $code). Fix what it reports and post again.
163
+ $warnings"
164
+ }
165
+
166
+ commands=$(printf '%s' "$cmd" | awk "$splitter")
167
+ while IFS= read -r line; do
168
+ [[ $line =~ $starts_gh_api ]] || continue
169
+ check "$line"
170
+ done <<<"$commands"
171
+ exit 0
@@ -27,6 +27,15 @@
27
27
  "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/scratch-guard.sh"
28
28
  }
29
29
  ]
30
+ },
31
+ {
32
+ "matcher": "Bash",
33
+ "hooks": [
34
+ {
35
+ "type": "command",
36
+ "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/review-post-scan.sh"
37
+ }
38
+ ]
30
39
  }
31
40
  ],
32
41
  "PostToolUse": [
@@ -1,14 +0,0 @@
1
- ---
2
- # Generated by `canon gov regen` from standards/context.md. Edit that frontmatter, not this file.
3
- description: Shape and content rules for canon/context/<domain>.md entries
4
- paths:
5
- - 'canon/context/**'
6
- ---
7
-
8
- # Context entry standards
9
-
10
- ## Authority
11
-
12
- - Follow the context standard for shape and content rules for canon/context/<domain>.md entries. It is the single source. Read it with `canon standards context`.
13
- - Before non-trivial work in a domain, read its `canon/context/<domain>.md` entry. Use `canon/context/index.md` to find it.
14
- - Leave the entry conforming when work in its domain changes what it describes.
@@ -1,12 +0,0 @@
1
- ---
2
- # Generated by `canon gov regen` from standards/design.md. Edit that frontmatter, not this file.
3
- description: Shape and content rules for canon/DESIGN.md
4
- paths:
5
- - 'canon/DESIGN.md'
6
- ---
7
-
8
- # Design standards
9
-
10
- ## Authority
11
-
12
- - Follow the design standard for shape and content rules for canon/DESIGN.md. It is the single source. Read it with `canon standards design`.