@erclx/canon 5.38.0 → 5.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/README.md +5 -5
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/SKILL.md +12 -9
  4. package/claude/skills/auto-ship/SKILL.md +43 -23
  5. package/claude/skills/backlog-triage/SKILL.md +62 -53
  6. package/claude/skills/backlog-triage/references/apply.md +69 -0
  7. package/claude/skills/build-in-slices/SKILL.md +8 -4
  8. package/claude/skills/canon-cli/SKILL.md +5 -1
  9. package/claude/skills/canon-feedback/SKILL.md +18 -8
  10. package/claude/skills/canon-feedback-triage/SKILL.md +10 -3
  11. package/claude/skills/canvas-board/SKILL.md +37 -18
  12. package/claude/skills/ci-workflow/SKILL.md +5 -4
  13. package/claude/skills/code-craft/SKILL.md +10 -6
  14. package/claude/skills/codebase-layout/SKILL.md +3 -5
  15. package/claude/skills/context-fold/REQUIREMENT.md +4 -4
  16. package/claude/skills/context-fold/SKILL.md +42 -23
  17. package/claude/skills/context-fold/references/context-refresh.md +4 -3
  18. package/claude/skills/context-fold/references/context.md +79 -64
  19. package/claude/skills/create-rule/SKILL.md +7 -3
  20. package/claude/skills/create-skill/SKILL.md +27 -7
  21. package/claude/skills/create-skill/references/skill-reference.md +41 -17
  22. package/claude/skills/decision-escalate/SKILL.md +8 -5
  23. package/claude/skills/deploy-app/SKILL.md +1 -1
  24. package/claude/skills/deprecation-migration/SKILL.md +12 -4
  25. package/claude/skills/design-taste/SKILL.md +17 -11
  26. package/claude/skills/docs-sync/REQUIREMENT.md +1 -0
  27. package/claude/skills/docs-sync/SKILL.md +17 -6
  28. package/claude/skills/docs-sync/references/docs.md +54 -21
  29. package/claude/skills/docs-sync/references/readme.md +32 -8
  30. package/claude/skills/document-health/SKILL.md +3 -4
  31. package/claude/skills/draft-and-pick/SKILL.md +73 -47
  32. package/claude/skills/draft-doc/SKILL.md +9 -4
  33. package/claude/skills/draft-doc/references/context.md +4 -4
  34. package/claude/skills/draft-doc/references/docs.md +1 -0
  35. package/claude/skills/draft-doc/references/wiki.md +3 -2
  36. package/claude/skills/draft-figure/SKILL.md +9 -7
  37. package/claude/skills/draft-identity/SKILL.md +24 -52
  38. package/claude/skills/draft-identity/references/card-route.md +46 -0
  39. package/claude/skills/draft-ready/SKILL.md +19 -7
  40. package/claude/skills/draft-screencast/SKILL.md +6 -112
  41. package/claude/skills/draft-screencast/references/beats-template.md +119 -0
  42. package/claude/skills/draft-slides/SKILL.md +9 -7
  43. package/claude/skills/draft-slides/references/deck.md +183 -0
  44. package/claude/skills/git-branch/SKILL.md +3 -3
  45. package/claude/skills/git-commit/SKILL.md +5 -3
  46. package/claude/skills/git-followup/SKILL.md +16 -9
  47. package/claude/skills/git-issue/SKILL.md +6 -5
  48. package/claude/skills/git-pr/SKILL.md +56 -44
  49. package/claude/skills/git-pr/references/evidence.md +2 -2
  50. package/claude/skills/git-pr/references/final-command.md +1 -1
  51. package/claude/skills/git-pr/references/labels.md +1 -1
  52. package/claude/skills/git-pr/references/publish.md +1 -1
  53. package/claude/skills/git-ship/SKILL.md +43 -34
  54. package/claude/skills/git-ship/references/ci-watch.md +31 -0
  55. package/claude/skills/git-split/SKILL.md +28 -100
  56. package/claude/skills/git-split/references/independent.md +40 -0
  57. package/claude/skills/git-split/references/stacked.md +73 -0
  58. package/claude/skills/git-stage/SKILL.md +9 -7
  59. package/claude/skills/git-worktree/SKILL.md +27 -49
  60. package/claude/skills/git-worktree/references/cleanup.md +55 -0
  61. package/claude/skills/markdown-craft/SKILL.md +2 -2
  62. package/claude/skills/markdown-craft/references/markdown.md +82 -53
  63. package/claude/skills/plan-feature/SKILL.md +52 -46
  64. package/claude/skills/plan-feature/references/batches.md +46 -0
  65. package/claude/skills/plan-groundwork/SKILL.md +60 -74
  66. package/claude/skills/plan-groundwork/references/output.md +48 -0
  67. package/claude/skills/plan-intake/SKILL.md +28 -21
  68. package/claude/skills/plan-intake-answer/SKILL.md +14 -13
  69. package/claude/skills/read-frames/SKILL.md +2 -0
  70. package/claude/skills/record-screencast/SKILL.md +31 -11
  71. package/claude/skills/review-address/SKILL.md +76 -158
  72. package/claude/skills/review-address/references/rebase-only.md +34 -0
  73. package/claude/skills/review-craft/SKILL.md +27 -11
  74. package/claude/skills/review-pr/SKILL.md +91 -40
  75. package/claude/skills/review-ui/SKILL.md +63 -37
  76. package/claude/skills/review-ui/references/hosted-preview.md +30 -0
  77. package/claude/skills/role-orchestrator/SKILL.md +105 -74
  78. package/claude/skills/role-orchestrator/references/orchestrator-filing.md +26 -0
  79. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
  80. package/claude/skills/role-planner/SKILL.md +66 -64
  81. package/claude/skills/role-reviewer/SKILL.md +57 -46
  82. package/claude/skills/role-worker/SKILL.md +68 -63
  83. package/claude/skills/search-craft/SKILL.md +7 -5
  84. package/claude/skills/session-compact/SKILL.md +16 -11
  85. package/claude/skills/session-map/SKILL.md +31 -10
  86. package/claude/skills/session-relay/SKILL.md +32 -65
  87. package/claude/skills/session-resume/SKILL.md +18 -13
  88. package/claude/skills/session-worktree/SKILL.md +70 -42
  89. package/claude/skills/systematic-debugging/SKILL.md +6 -1
  90. package/claude/skills/target-check/SKILL.md +13 -6
  91. package/claude/skills/target-setup/SKILL.md +39 -27
  92. package/claude/skills/task-board/SKILL.md +68 -60
  93. package/claude/skills/task-board/references/board.md +1 -1
  94. package/claude/skills/task-board/references/create-report.md +36 -0
  95. package/claude/skills/task-board/references/tasks.md +1 -1
  96. package/claude/skills/task-complete/SKILL.md +20 -18
  97. package/claude/skills/teach-workspace/SKILL.md +61 -33
  98. package/claude/skills/test-craft/SKILL.md +2 -1
  99. package/claude/skills/test-first/SKILL.md +15 -9
  100. package/claude/skills/ui-checklist/SKILL.md +18 -16
  101. package/claude/skills/ux-audit/SKILL.md +9 -10
  102. package/claude/skills/ux-measure/SKILL.md +23 -19
  103. package/claude/skills/video-craft/SKILL.md +15 -7
  104. package/claude/skills/write-human/SKILL.md +9 -1
  105. package/claude/skills/youtube-transcripts/SKILL.md +2 -1
  106. package/docs/agents/audits.md +61 -60
  107. package/docs/agents/context-audit.md +40 -58
  108. package/docs/agents/index.md +11 -61
  109. package/docs/agents/indexes.md +25 -29
  110. package/docs/agents/install-and-sync.md +125 -235
  111. package/docs/agents/markdown-audit.md +54 -96
  112. package/docs/agents/migrate.md +41 -16
  113. package/docs/agents/output-shape.md +60 -45
  114. package/docs/agents/overview.md +36 -12
  115. package/docs/agents/records.md +37 -79
  116. package/docs/agents/sessions-transfer.md +26 -70
  117. package/docs/agents/targets.md +25 -77
  118. package/docs/target/index.md +0 -1
  119. package/docs/target/migrations.md +57 -66
  120. package/docs/target/projects.md +36 -78
  121. package/docs/target/scenarios.md +9 -19
  122. package/docs/target/sync.md +27 -68
  123. package/docs/workflow/ai-workflow.md +22 -68
  124. package/docs/workflow/operating-model.md +32 -186
  125. package/docs/workflow/parallel-features.md +29 -66
  126. package/docs/workflow/skill-map-on-request.md +22 -24
  127. package/docs/workflow/skill-map.md +72 -76
  128. package/docs/workflow/visual-design-workflow.md +25 -118
  129. package/docs/workflow/zshrc-aliases.md +18 -37
  130. package/package.json +1 -1
  131. package/src/canvas/client/inspector/fields.css +4 -0
  132. package/src/canvas/index.html +6 -0
  133. package/src/claude/skills-reach.ts +2 -2
  134. package/src/cli/ui.ts +3 -1
  135. package/src/commands/canvas.ts +23 -0
  136. package/src/commands/capture.ts +14 -0
  137. package/src/commands/claude.ts +14 -1
  138. package/src/commands/context.ts +106 -7
  139. package/src/commands/demo.ts +22 -0
  140. package/src/commands/deps.ts +6 -0
  141. package/src/commands/driver.ts +28 -2
  142. package/src/commands/gov.ts +52 -18
  143. package/src/commands/hooks.ts +63 -0
  144. package/src/commands/intake.ts +17 -0
  145. package/src/commands/labels.ts +38 -5
  146. package/src/commands/markdown.ts +169 -32
  147. package/src/commands/pr.ts +160 -43
  148. package/src/commands/records.ts +8 -0
  149. package/src/commands/secrets.ts +6 -0
  150. package/src/commands/sessions.ts +8 -0
  151. package/src/commands/tasks.ts +52 -0
  152. package/src/commands/teach.ts +13 -0
  153. package/src/commands/upstream.ts +25 -1
  154. package/src/commands/worktrees.ts +5 -0
  155. package/src/context/audit.ts +274 -5
  156. package/src/context/classify/prompts.ts +1 -1
  157. package/src/context/folders.ts +1 -1
  158. package/src/context/gate.ts +23 -7
  159. package/src/context/shape-gated.ts +30 -0
  160. package/src/counts/scan.ts +1 -1
  161. package/src/design/components.ts +1 -1
  162. package/src/design/document.ts +1 -1
  163. package/src/design/tokens.ts +1 -1
  164. package/src/gate/boundaries.ts +4 -1
  165. package/src/gate/measures.ts +16 -191
  166. package/src/gate/stages.ts +14 -30
  167. package/src/git/env.ts +6 -0
  168. package/src/hooks/reply-links.ts +110 -0
  169. package/src/markdown/density.ts +258 -0
  170. package/src/markdown/gate.ts +57 -18
  171. package/src/markdown/reference-shape.ts +103 -0
  172. package/src/markdown/scope.ts +162 -0
  173. package/src/markdown/structure.ts +9 -59
  174. package/src/pr/lift.ts +153 -19
  175. package/src/pr/painted.ts +87 -0
  176. package/src/roots/record.ts +18 -1
  177. package/src/sandbox/census.ts +1 -1
  178. package/src/sandbox/headless/run.ts +3 -2
  179. package/src/sandbox/headless/watch.ts +3 -4
  180. package/src/shipped/references.ts +1 -1
  181. package/src/slides/convert/master.ts +3 -0
  182. package/src/sync/format.ts +46 -0
  183. package/src/sync/stamp.ts +6 -0
  184. package/tooling/astro/reference.md +2 -2
  185. package/tooling/base/reference.md +1 -1
  186. package/tooling/claude/seeds/.claude/hooks/reply-links.sh +72 -0
  187. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +22 -1
  188. package/tooling/claude/seeds/.claude/settings.json +4 -0
  189. package/tooling/go/reference.md +1 -1
  190. package/tooling/nextjs/reference.md +1 -1
  191. package/tooling/php/reference.md +1 -1
  192. package/tooling/python/reference.md +1 -1
  193. package/tooling/vite-react/reference.md +1 -1
  194. package/tooling/web/reference.md +1 -1
  195. package/docs/agents/canvas.md +0 -86
  196. package/docs/agents/capture.md +0 -65
  197. package/docs/agents/census.md +0 -23
  198. package/docs/agents/commands.md +0 -154
  199. package/docs/agents/comments.md +0 -34
  200. package/docs/agents/context-audit-checks.md +0 -90
  201. package/docs/agents/context-classify.md +0 -101
  202. package/docs/agents/counts.md +0 -74
  203. package/docs/agents/demo.md +0 -140
  204. package/docs/agents/docs.md +0 -27
  205. package/docs/agents/drift-surfaces.md +0 -201
  206. package/docs/agents/driver.md +0 -103
  207. package/docs/agents/gate.md +0 -90
  208. package/docs/agents/gov-detect.md +0 -47
  209. package/docs/agents/hooks.md +0 -58
  210. package/docs/agents/intake.md +0 -81
  211. package/docs/agents/key-changes.md +0 -116
  212. package/docs/agents/label-coverage.md +0 -86
  213. package/docs/agents/markdown-audit-weight.md +0 -81
  214. package/docs/agents/pr-evidence-layout.md +0 -70
  215. package/docs/agents/pr-evidence.md +0 -273
  216. package/docs/agents/pr-frames.md +0 -131
  217. package/docs/agents/pr-lift.md +0 -132
  218. package/docs/agents/pr-local.md +0 -111
  219. package/docs/agents/pr-preview.md +0 -155
  220. package/docs/agents/pr-reads.md +0 -111
  221. package/docs/agents/pr-tick.md +0 -72
  222. package/docs/agents/records-push.md +0 -61
  223. package/docs/agents/records-stale.md +0 -40
  224. package/docs/agents/records-validate.md +0 -57
  225. package/docs/agents/restated.md +0 -83
  226. package/docs/agents/routing.md +0 -61
  227. package/docs/agents/rule-citations.md +0 -90
  228. package/docs/agents/sandbox.md +0 -126
  229. package/docs/agents/scripting.md +0 -142
  230. package/docs/agents/sessions.md +0 -128
  231. package/docs/agents/skills-audit.md +0 -120
  232. package/docs/agents/skills-reach.md +0 -64
  233. package/docs/agents/slides.md +0 -176
  234. package/docs/agents/state-scoped-risk.md +0 -105
  235. package/docs/agents/superseded.md +0 -85
  236. package/docs/agents/tasks-archive.md +0 -83
  237. package/docs/agents/tasks-plans.md +0 -152
  238. package/docs/agents/tasks-validate.md +0 -133
  239. package/docs/agents/tasks.md +0 -123
  240. package/docs/agents/teach-authoring.md +0 -118
  241. package/docs/agents/teach.md +0 -157
  242. package/docs/agents/test-order.md +0 -58
  243. package/docs/agents/upstream.md +0 -94
  244. package/docs/agents/worktrees.md +0 -118
  245. package/docs/target/standards-moved.md +0 -34
  246. package/src/markdown/growth.ts +0 -43
@@ -1,201 +0,0 @@
1
- ---
2
- title: Drift surfaces
3
- description: The seven sections canon sync --check reports beside the per-domain scan, being seeds, superseded artifacts, unmigrated domains, surfaces at the old root, new rules and skills, and the reverse walk, with the managed-target gate and what counts toward the exit code
4
- ---
5
-
6
- # Drift surfaces
7
-
8
- `canon sync --check` reports seven sections outside the per-domain scan that
9
- `install-and-sync.md` describes, because each names something
10
- that walk cannot see: `seeds`, `superseded`, `unmigrated`, `unmovedSurfaces`,
11
- `newSkills`, `newRules`, and `reverse`. None of them produces a change, and no
12
- sync command applies any of them. The headings below group the first four
13
- together and give `newSkills` its description under `newRules`, which answers the
14
- same question about a different corpus.
15
-
16
- All seven report only against a toolkit-managed target, which is one carrying a
17
- `.claude/` directory, a `CLAUDE.md`, or a domain still at the root layout. The
18
- report says so through `managed` in the JSON and routes an unmanaged directory to
19
- `canon init`. Seeds are why the gate exists, since they enumerate from the toolkit
20
- source rather than from what a target installed, so an unmanaged directory would
21
- otherwise report every seed as `missing`.
22
-
23
- A root-layout domain counts as a marker on its own, because the detection fires
24
- only on root files the toolkit ships and a project in the old layout is one the
25
- toolkit installed. When `managed` is false every section comes back empty rather
26
- than the render alone going quiet, so a consumer reading `--json` never acts on a
27
- finding the rendered half withheld.
28
-
29
- ## Seeds, superseded artifacts, unmigrated domains, and old-root surfaces
30
-
31
- `seeds` classifies every seed the toolkit ships against the target's copy, as
32
- `matching`, `stale`, `drifted`, or `missing`. `missing` has no per-domain
33
- equivalent, since the domain walk lists what a target installed and cannot see a
34
- file that never arrived. There is no `customized` verdict here, because that one
35
- needs a stamp and seeds carry none, so a file history cannot attribute stays
36
- `drifted`. Merge the section by hand against the toolkit copy rather than
37
- replacing a file the project edits.
38
-
39
- A markdown seed installs rewritten rather than copied, since the `stub: true`
40
- marker the toolkit's own seed gate reads is stripped on the way in. The
41
- comparison above runs against what the install would write, so a marked seed a
42
- target never touched still reports `matching`. Every other seed copies byte for
43
- byte.
44
-
45
- `superseded` names a file a newer seed folder replaced, such as `.claude/TASKS.md`
46
- against the `.canon/tasks/` that now ships. The entry carries `replacedBy` and
47
- nothing else, and the file is never deleted, since the content belongs to the
48
- project and only its author can decide where it moves. The list derives from the
49
- seed tree rather than from a fixed set of filenames, so a folder added later is
50
- covered without a code change. Only an exact stem matches, which leaves a
51
- suffixed variant such as `TASKS-ARCHIVE.md` unreported.
52
-
53
- No skill splits the file. The destination's shape lives in the skill that owns
54
- it, so the split is the project's own to make.
55
-
56
- `unmigrated` names a domain sitting at the root layout an older toolkit installed
57
- to, with nothing at the path the current one reads. It carries `rootPath`,
58
- `installPath`, and a file count. Without it a project holding an older layout at its
59
- root reports zero entries for that domain and reads as clean, which is the most
60
- misleading state the report can produce.
61
-
62
- The field currently names no domain. Snippets was the one the toolkit ever
63
- installed at a project root, and it retired outright. A leftover `snippets/`
64
- folder reports under the reverse walk instead, where `docs/target/sync.md` says
65
- what to do with it.
66
-
67
- `unmovedSurfaces` names a tracked surface the target still holds at its `.claude/`
68
- spelling with nothing at the `canon/` one, such as `.claude/REQUIREMENTS.md` or
69
- `.claude/context/`. Each entry carries `rel` and `movesTo`, and the render names
70
- `canon migrate surface-roots`, the verb that moves them. Rules glob the `canon/`
71
- spelling alone, so a target on the old root stops loading the rules scoped to
72
- those files until it moves. <!-- canon-keep-surface-root -->
73
-
74
- ## Rules the target never received
75
-
76
- `newRules` names a rule the target could receive and its tree does not hold. A
77
- sync refreshes the files a target already holds and adds none, so without this
78
- section a project's rule set freezes at its install date while every file it
79
- does hold reports as current. That is the report's most confident wrong
80
- answer, since a clean result reads as a target holding everything the toolkit
81
- publishes.
82
-
83
- The list rides beside `newSkills`, which asks the same question about the plugin
84
- catalog. Both are names rather than paths, and neither queues a change, because
85
- the two remedies differ: a skill loads live from the plugin directory and needs
86
- nothing run, while a rule reaches a target only when someone runs
87
- `canon gov install`.
88
-
89
- Read a clean section as reporting rather than as delivering. Nothing here
90
- installs, nothing counts toward `--exit-code`, and a target can read the list and
91
- act on none of it. The value stops at an operator reading it, which is the same
92
- contract the skills list already sets.
93
-
94
- Since `canon gov install` records the stack it resolved, a target carrying that
95
- record answers this by comparing its current entitlement against its current
96
- tree, with no anchor and no git diff involved. That is what lets the section
97
- name a rule that shipped before the target's last sync: the anchor a sync
98
- advances plays no part in the read, where an anchor-bound diff can never see a
99
- rule on the far side of a window a later sync moved past it. The per-file
100
- `missing` state `canon gov sync` reports comes from the same comparison, so a
101
- rule the chain lists reaches both surfaces the same way.
102
-
103
- A target stamped before governance recorded a chain falls back to the older
104
- rule-folder-inference read below. The measurement there anchors on governance's own
105
- stamp rather than on the oldest anchor across domains, since rules are
106
- domain-scoped and a shared anchor would let another domain's sync move the
107
- revision rules are measured from. A target carrying no chain and no governance anchor
108
- reports nothing at all: it has no date to measure against, and diffing from the
109
- start of history would read the whole catalog as new.
110
-
111
- An anchor this toolkit cannot resolve reports nothing by the same route, and that
112
- one is not visible. A stamp naming a revision the running clone has never seen,
113
- which is what a registry install without history or a shallow clone produces,
114
- fails the read and yields an empty list rather than a stated absence. It looks
115
- identical to a target holding every rule the toolkit publishes. `newSkills`
116
- behaves the same way, and neither carries the `historyUnavailable` flag the
117
- per-domain scan uses to separate the two. Treat an empty section on a toolkit
118
- that is not a full clone as unmeasured rather than clean. This gap does not
119
- reach the chain-based read above, since it consults no anchor at all.
120
-
121
- In the fallback, entitlement is filtered, because a stack does not receive
122
- every rule. The base stack names ten rules, the language stacks add `code` and
123
- their own language and testing rules, and every other rule is opt-in, so an
124
- unfiltered list would tell a base consumer about rules it can never receive and
125
- train the reader to skip the section.
126
-
127
- The filter accepts a rule folder on either of two grounds. A folder holding a rule the
128
- base stack resolves to is entitled to every target, read through the stack file so
129
- a rule added to base later needs no code change. Every other rule folder is read off the folders the
130
- target already carries, which is the fallback's only evidence of what a target
131
- was entitled to before it recorded a chain. One rule folder can be reached by more than
132
- one stack, so the test over-reports inside a folder the target holds, which
133
- costs a line where under-reporting would cost the section its point.
134
-
135
- A rule the target already holds is dropped by name. That is what keeps a rule the
136
- toolkit moved between rule folders out of the list, since a rename reaches this
137
- read as an addition and only the name tells the two apart.
138
-
139
- This closes the dangling-citation case as a side effect rather than checking for
140
- one. The live instance is a rule citing a sibling authored after the target's
141
- install, and the section names the sibling as new rather than naming the citation
142
- as broken. That is enough for an operator to act on, and it is not a citation
143
- check.
144
-
145
- ## The reverse walk
146
-
147
- `reverse` is the one section built by walking the target rather than the
148
- catalog. Every other surface enumerates toolkit-owned keys and asks whether the
149
- target matches, so a folder the toolkit deleted appears in none of them. It
150
- carries `unclaimed` and `historyUnavailable`.
151
-
152
- `unclaimed` names a folder the target holds at a top-level path the toolkit once
153
- shipped and has since deleted. The candidate roots come from the toolkit's own
154
- history rather than from a list, so a root dropped later is covered without a
155
- code change. Scoping to those roots is what keeps the walk useful: walking the
156
- whole tree reports every project folder as unclaimed, which is true and says
157
- nothing.
158
-
159
- The managed gate above applies here too, and it is the one place it surprises.
160
- A directory holding a dropped folder and nothing else reports an empty `reverse`
161
- rather than the folder, because it carries none of the three markers. Read an
162
- empty section on an unmanaged target as a walk that never ran rather than as a
163
- clean result.
164
-
165
- Each entry carries `rel`, a file count, and an `attribution` of `dropped`,
166
- `project`, or `unattributed`. A dropped folder and one the project wrote are the
167
- same bytes at the same path, so the verdict is traced from history rather than
168
- guessed from the filesystem.
169
-
170
- Content matching a version the toolkit published reads as `dropped` and carries
171
- the `since` commit that published it. Names the toolkit shipped holding content
172
- it never published read as `unattributed`, which is a state in its own right
173
- rather than a soft yes. No overlap at all reads as `project`, and the render
174
- drops those while the JSON keeps them.
175
-
176
- Only files whose path the toolkit once held are hashed, so a project folder
177
- colliding on a retired name costs the walk no reads. The cost is that a file the
178
- toolkit shipped and the target renamed goes unmatched, the same limit the
179
- `unmigrated` count carries.
180
-
181
- ## What counts toward the gate
182
-
183
- `unmigrated` counts toward `--exit-code`, since running the relocation closes it.
184
- `superseded` and every seed state are excluded, for the reason `orphaned` already
185
- is: only the user can move content they wrote, so failing a job on it leaves the
186
- job red with no mechanical remedy.
187
-
188
- `unmovedSurfaces` is excluded although `canon migrate surface-roots` closes it.
189
- The move rewrites citations across the project, so it is a change the project
190
- schedules, and a job counting it would go red the day a target updated the CLI.
191
-
192
- `newRules` is excluded on a different ground, since a command does close it. What
193
- excludes it is that installing a rule changes what a project is governed by, so
194
- gating on the count would pressure a target into adopting rules nobody chose.
195
- `newSkills` is excluded because it needs no command at all.
196
-
197
- The whole `reverse` section is excluded on the same grounds, and more strongly.
198
- Every entry in it is a judgment about a file the project may own, and one of its
199
- three verdicts is a labelled unknown by design. The unmigrated detection shipped
200
- that exact false positive once, failing a push with no action that cleared it,
201
- so this section reports and gates nothing.
@@ -1,103 +0,0 @@
1
- ---
2
- title: Driver
3
- description: Walking a page through named interactions, the probe catalog and the false finding each one carries, why viewport heights are never defaulted, and what each refusal reports
4
- ---
5
-
6
- # Driver
7
-
8
- `canon drive <url> <run>` walks a page through a sequence of interactions and measures every state it reaches. A render answers about a page as it loads, so a defect that exists only after a menu opens, an answer is chosen, or the page scrolls is invisible to one. That second axis is what this adds.
9
-
10
- ```bash
11
- canon drive http://localhost:4173 run.json
12
- canon drive http://localhost:4173 run.json --json
13
- ```
14
-
15
- | Option | Behavior |
16
- | -------- | --------------------------------------- |
17
- | `--json` | Add a machine-readable record on stdout |
18
-
19
- ## The run file
20
-
21
- The run is a JSON file rather than a key in a project config, because a route catalog is state a project holds and an interaction sequence is a script written for one question. It carries the viewport, the probes to run by default, and the steps.
22
-
23
- ```json
24
- {
25
- "viewport": { "width": 1440, "heights": [900, 1200, 1500] },
26
- "probes": ["focus", "details"],
27
- "steps": [
28
- { "name": "on load", "kind": "wait", "ms": 0 },
29
- { "name": "open the menu", "kind": "click", "target": "#menu summary" },
30
- {
31
- "name": "reach the diagram",
32
- "kind": "scroll",
33
- "target": "figure",
34
- "probes": ["diagram-geometry", "diagram-strokes"]
35
- }
36
- ]
37
- }
38
- ```
39
-
40
- Every step names what it did, and that name is carried onto each finding it produced, alongside the viewport. A step naming its own `probes` overrides the run-level list for that step alone. Omitting `probes` at the run level runs all four.
41
-
42
- | Step kind | Fields | What it does |
43
- | --------- | ------------------ | ---------------------------------------------- |
44
- | `click` | `target` | Presses the first element the selector matches |
45
- | `scroll` | `target` | Brings the first match into view |
46
- | `fill` | `target`, `text` | Types into the first match |
47
- | `tab` | `count` (optional) | Advances keyboard focus, once by default |
48
- | `wait` | `ms` | Holds, for a state the page reaches alone |
49
-
50
- Probes run after a step and never on arrival, so a run reaches the load state by opening with a `wait` step of its own, as the example above does. Nothing probes before the first step runs, which makes that leading step the only way to measure the page as it first painted, and naming it is what puts the load state on its own findings rather than under whatever ran next.
51
-
52
- Write one wherever no capture runs against the page. `canon capture` renders a single state from a source on disk or a URL, so where it runs it already answers about arrival and a leading `wait` duplicates it. Where it does not, this command is the only thing measuring the page at all, and a run without that step reports every driven state and nothing about the one a visitor sees first.
53
-
54
- Each height is driven in a context of its own from a fresh navigation, rather than by resizing the page the previous height already drove, since a sweep asks the same question of each height rather than a later question of an already-driven page.
55
-
56
- ## The probes
57
-
58
- Four probes, a fixed catalog rather than a subject system a project extends. Each carries a class of false finding that a throwaway version of this command produced before it produced true ones, and a probe a project authored for itself would re-pay those.
59
-
60
- | Probe | What it reports |
61
- | ------------------ | ------------------------------------------------------------------------------------------ |
62
- | `focus` | An element that takes keyboard focus and changes nothing visible |
63
- | `details` | A `details` panel off the viewport, or a row under the tap minimum, both read open |
64
- | `diagram-geometry` | An SVG label outside its frame, covered by a filled shape, or colliding with another label |
65
- | `diagram-strokes` | An SVG label a stroke crosses, sampled along the stroke's own geometry |
66
-
67
- `focus` presses Tab before it reads anything. `:focus-visible` does not match a scripted `.focus()` once the page has taken a pointer interaction, and a driver clicks by definition, so a reader without that press reports every correctly styled element as unstyled. It also ignores an outline width, color, or offset moving underneath an outline that resolves to `none`, which paints nothing.
68
-
69
- `details` discovers every `details` on the page rather than taking a selector, measures each shut and again open, and judges only the open reading. A shut menu still reports a layout box and the box describes the trigger rather than the panel, so the shut numbers travel in the record as context. Discovery is automatic because the one real menu defect in the originating run was missed by a hand-picked probe: nobody thought to name that menu.
70
-
71
- Both diagram probes read after `document.fonts.ready` and never compute from the markup. A generated page is routinely authored against one font and restyled to another, so a label that cleared a line at its authored width can overlap it once rendered. Every rect either probe compares is inset vertically and never horizontally, since a text rect is loose above and below the glyphs and tight to them left and right, and a horizontal inset passes a real overrun.
72
-
73
- `diagram-strokes` is separate from `diagram-geometry` because a bounding box cannot answer for a stroke. A probe comparing filled boxes filters out every `line` and every `fill="none"` panel border before it compares, and a diagonal connector's box covers most of a diagram while the stroke itself touches almost none of it. Both paint orders are findings: a stroke drawn after a label crosses the glyphs out, and one drawn before it shows through them, since SVG text carries no plate of its own.
74
-
75
- ## Viewport heights are never defaulted
76
-
77
- A run with no `viewport.heights` refuses. The heights that separate a passing render from a failing one belong to the layout being driven, and the only evidence behind any default this could ship is one fixture's failure range, too narrow a sample to hand every later caller a number they never chose. A scroll rail that skipped its middle sections passed at 900 and failed at 1200 and 1500, so one height reports clean over a live defect.
78
-
79
- ## What it reports
80
-
81
- It reports findings and never gates, and the exit code says only whether the drive completed. Every probe here carries a class of false finding already paid for once, so a command ending a build on its own reading makes a claim this catalog has not earned. A caller that wants a verdict reads `findings` off the `--json` record and decides for itself.
82
-
83
- | Reason | What it means |
84
- | -------------------- | ------------------------------------------------------ |
85
- | `no-run-file` | Nothing readable at the path given |
86
- | `unreadable-plan` | The run file is not valid JSON |
87
- | `no-steps` | The run names no step |
88
- | `no-probes` | The run names an empty probe list |
89
- | `unknown-probe` | The run names a probe this build does not ship |
90
- | `bad-step` | A step is missing what the driver needs to perform it |
91
- | `no-viewport` | The run declares no viewport |
92
- | `no-width` | The viewport has no width to wrap at |
93
- | `no-heights` | The viewport names no height, which is never defaulted |
94
- | `engine-missing` | The browser engine is not installed in this project |
95
- | `browser-missing` | The engine is installed and its browser binary is not |
96
- | `server-unreachable` | Nothing answered at the URL, so no state was reached |
97
- | `drive-failed` | The drive failed against a reachable page |
98
-
99
- An unreachable page refuses rather than returning an empty report, since nothing measured and nothing found read the same to anything counting findings.
100
-
101
- The browser binary installs separately from the package, once, with `bunx playwright install chromium`. The command ships to targets like `demo`, `inventory`, and `capture`, because its whole purpose is measuring a page inside someone else's project. It reaches nothing under `src/capture/`, so the two surfaces move independently.
102
-
103
- See `commands.md` for where this sits among the browser commands, and `capture.md` for the single-state render it adds an axis to.
@@ -1,90 +0,0 @@
1
- ---
2
- title: Merge gate
3
- description: Running the gate this repository verifies a branch with, what the stage table holds and what stays a script, how the changed set scopes three stages, and why a stage that cannot read its input reports rather than passing
4
- ---
5
-
6
- # Merge gate
7
-
8
- `canon gate run` runs every stage that guards a branch here, in order, stopping at the first stage that finds a fact. `bun run check` and `bun run check:ci` resolve to it, and the `update` package script runs `bun run check` after a dependency update.
9
-
10
- ```bash
11
- canon gate run
12
- canon gate run --all
13
- canon gate run --json
14
- ```
15
-
16
- | Option | Behavior |
17
- | ---------- | ----------------------------------------------------------------------------- |
18
- | `--all` | Run every stage instead of scoping shell, types, and tests to the changed set |
19
- | `--nested` | Suppress the outer frame when a calling script has already opened one |
20
- | `--json` | Add a machine-readable record on stdout |
21
-
22
- ## What the command owns and what it runs
23
-
24
- Three things sit in the command: the stage table in `src/gate/stages.ts`, the changed-file scoping and the run loop in `src/gate/sequencer.ts`, and every threshold comparison in `src/gate/measures.ts`. Each individual check is the script or the verb it already was, under `scripts/core/` or behind another `canon` command, and the move changed none of them. Sequencing, scoping, and comparison are where the recurring defects were, and a check whose behavior changed while its sequencing moved would make any regression impossible to attribute.
25
-
26
- A stage is a list of checks and a check is one of four kinds:
27
-
28
- | Kind | What it is |
29
- | --------- | ------------------------------------------------------------------------------- |
30
- | `command` | Any binary, run from the project root |
31
- | `cli` | This checkout's own `src/cli.ts`, never a globally installed `canon` |
32
- | `drift` | A regenerated pathspec asserted against the index and against the untracked set |
33
- | `measure` | A reading whose verdict is a comparison rather than an exit code |
34
-
35
- A `cli` check runs the source rather than the binary because a globally installed `canon` resolves to the main checkout no matter which worktree is running, so a gate reading through it would measure the wrong tree and pass a branch it never opened.
36
-
37
- Every check is an argument vector rather than a shell line, so no stage carries a quoting hazard and a `drift` pathspec reaches git exactly as the table spells it.
38
-
39
- ## Scoping
40
-
41
- Shell, types, and tests read the changed set. Everything else always runs, because its input is diffuse enough that no path predicts it.
42
-
43
- The changed set unions the branch diff against the merge base with `origin/main`, the working tree, and untracked files, which is what a pull request will contain. The baseline is the remote ref and not local `main`, since on `main` itself the local ref is HEAD and every unpushed commit would drop out. Every fallback widens rather than narrows: no merge base at all runs every stage, and a local baseline equal to HEAD does the same. `--all` turns scoping off outright, which is what `bun run check:ci` passes so CI stays the backstop for a wrong local scoping decision.
44
-
45
- ## A stage that cannot read its input
46
-
47
- A stage reports one of four states. It passed, it was scoped out, it found a fact, or it could not measure its input at all.
48
-
49
- The fourth is the one worth naming. An absent tool, a catalog that did not report, a corpus with nothing under it: each used to print a line that read like a pass. Now the run records it as unmeasured, the closing line says how many stages measured nothing, and the reader is not told a verdict nobody took.
50
-
51
- What happens next depends on where the run is. On a contributor's machine it warns and the run still exits 0, because an absent tool there is somebody mid-setup. Under CI, read off `CI=true`, it refuses, because the same absence on a runner is a broken workflow step.
52
-
53
- ## Exit codes
54
-
55
- | Code | Meaning |
56
- | ---- | ------------------------------------------------------------- |
57
- | `0` | every stage that ran reported, and none found a fact |
58
- | `1` | a stage found a fact, or could not measure its input under CI |
59
-
60
- One code for a failure, which is what a `bun run` caller and a git hook both read. An unmeasured stage takes no code of its own, since it has already refused under CI and reports on a contributor's machine, so a second code would name a state no caller branches on.
61
-
62
- ## The record
63
-
64
- `--json` puts one record on stdout and keeps every diagnostic on stderr:
65
-
66
- ```json
67
- {
68
- "ok": true,
69
- "root": "/path/to/checkout",
70
- "scoped": true,
71
- "changed": 12,
72
- "summary": {
73
- "ran": 23,
74
- "passed": 22,
75
- "skipped": 1,
76
- "unmeasured": 0,
77
- "failed": 0
78
- },
79
- "ms": 44453,
80
- "stages": [
81
- { "id": "indexes", "label": "Indexes", "status": "passed", "ms": 210 }
82
- ]
83
- }
84
- ```
85
-
86
- A failing stage carries its remedy in `failure`, and the same line goes to stderr so a caller reading neither the record nor the frame is still told what to fix.
87
-
88
- `ms` carries the wall time each stage took, including every process spawn, and the top-level `ms` sums them. Neither reading changes what a stage measures. Both exist so a slow run is attributable to a stage rather than read off a stopwatch held against the whole thing.
89
-
90
- The Tests stage holds a machine lock so one box runs one suite at a time, and its `ms` includes any time it queued behind another worktree's suite. A stage that queued carries `queuedMs`, the part of `ms` spent waiting, which is how to separate a queue from a slow suite. The field is absent on a stage that never waited. The wait is also announced on stderr when it starts, naming the holder's pid and worktree, in every mode.
@@ -1,47 +0,0 @@
1
- ---
2
- title: Rule detection
3
- description: Proposing opt-in governance rules from a target's dependencies and paths, the evidence file the verb reads, the record it emits, and its exit codes
4
- ---
5
-
6
- # Rule detection
7
-
8
- `canon gov detect` proposes the opt-in governance rules a target's evidence points at, and writes nothing. A rule no stack names reaches a project only through this verb, which `canon:target-setup` calls before it previews an install.
9
-
10
- ```bash
11
- canon gov detect
12
- canon gov detect ../my-app --stack node --json
13
- ```
14
-
15
- | Option | Behavior |
16
- | ----------------- | ------------------------------------------------ |
17
- | `--stack <stack>` | Leave out every rule this stack already resolves |
18
- | `--json` | Emit the record on stdout instead of the frame |
19
-
20
- ## Evidence
21
-
22
- Each opt-in rule keeps a table in the toolkit's `governance/detect.toml`, keyed by rule basename. Its `deps` are package names, read from `package.json` (`dependencies` and `devDependencies`), `pyproject.toml`, `go.mod`, and `composer.json`. Its `files` are globs matched against the target's git-listed paths and against each folder on those paths, so `.github/workflows` matches a folder holding a workflow. A glob with no wildcard is also tested on disk, which is how `.canon/canvas` matches in a target that gitignores `.canon/`.
23
-
24
- Both reads stop at depth two, which reaches a service folder and a workspace package under `packages/<name>/`. Outside git the verb walks the directory to the same depth and skips `.git`, `node_modules`, `vendor`, and `.venv`. A manifest that does not parse contributes nothing.
25
-
26
- ## Record
27
-
28
- ```json
29
- {
30
- "target": "/abs/path/my-app",
31
- "stack": "node",
32
- "proposals": [
33
- {
34
- "rule": "react",
35
- "evidence": [{ "kind": "dep", "match": "react", "path": "package.json" }]
36
- }
37
- ]
38
- }
39
- ```
40
-
41
- `kind` is `dep` for a dependency, with `path` naming the manifest that declares it, or `file` for a glob, with `path` naming what it matched. `stack` is null when no `--stack` was passed.
42
-
43
- ## Exit codes
44
-
45
- The verb exits 0 on every readable target, including one with no evidence, which returns an empty `proposals` array. A target that does not exist and a `--stack` that does not resolve each exit 1 with the reason on stderr.
46
-
47
- Install what the operator kept with `canon gov install --add <rules> <target>`, which needs no stack.
@@ -1,58 +0,0 @@
1
- ---
2
- title: Hooks
3
- description: Running the post-merge steps through one verb, the order and the child call behind each, the root and working directory each step reads, the skip switches, and the line each record maps to
4
- ---
5
-
6
- # Hooks
7
-
8
- `canon hooks post-merge` runs the steps a git `post-merge` hook drives, so the hook itself stays a guard plus one call. It reads which pull requests the merge brought in, runs each step as a child `canon` call with `--json`, and prints one line per outcome worth reporting.
9
-
10
- ```bash
11
- canon hooks post-merge
12
- canon hooks post-merge --root "$root"
13
- ```
14
-
15
- | Option | Behavior |
16
- | --------------- | ---------------------------------------------------------------------------------------------------- |
17
- | `--root <path>` | Main worktree root holding the board and the records, resolved from `git worktree list` when omitted |
18
-
19
- The base stack ships the same call as its `.husky/post-merge`, behind the same board guard and upgrade bootstrap, so a target that syncs it runs these five steps.
20
-
21
- Exit codes: `0` on every path, since a hook failing aborts nothing useful. Lines go to stderr, and stdout stays empty. An ordinary merge prints nothing.
22
-
23
- ## The steps
24
-
25
- The steps run in this order, and the verb's `--help` lists the child calls from the same table it runs them from.
26
-
27
- | Step | Child call | Runs from |
28
- | ------------------------ | ------------------------------------------------------------- | ---------------- |
29
- | Archive the merged task | `canon tasks archive --pull-request <n> --root <root> --json` | the caller's cwd |
30
- | Push the records | `canon records push --root <root> --json` | the caller's cwd |
31
- | Reclaim merged worktrees | `canon worktrees reclaim --json` | the caller's cwd |
32
- | Reinstall the CLI | `canon upgrade --json`, under `CANON_NON_INTERACTIVE=1` | the caller's cwd |
33
- | Update the plugin cache | `canon claude plugin-update --json` | the caller's cwd |
34
-
35
- The archive runs once per pull request number, read as the trailing `(#NNN)` on every subject in `ORIG_HEAD..HEAD`, or on the tip alone when the merge left no `ORIG_HEAD`.
36
-
37
- The reclaim takes no `--root`. It refuses the worktree its own working directory names, so a root would let a pull inside a linked worktree remove the worktree it stands in. The archive and the push take the root because the board and the records live at the main worktree root.
38
-
39
- Each step is a separate process rather than an in-process call, so the plugin update runs the binary the upgrade just installed. Every child, and every `git` read, runs with the repository variables a hook exports stripped, since `GIT_DIR` beats `-C`.
40
-
41
- ## Skip switches
42
-
43
- | Variable | Skips |
44
- | ---------------------------- | ----------------------- |
45
- | `CANON_SKIP_RECLAIM=1` | the worktree reclaim |
46
- | `CANON_SKIP_UPGRADE=1` | the CLI reinstall |
47
- | `CANON_SKIP_PLUGIN_UPDATE=1` | the plugin cache update |
48
-
49
- Any non-empty value skips the step, and each switch skips its own step only.
50
-
51
- ## What prints
52
-
53
- Each line is decided from the child's parsed record. A child producing no parseable record is an older binary carrying no such subcommand, and that step stays quiet.
54
-
55
- - Archive: the archived task and its number, plus a `Links not repointed in:` line naming the files in a non-empty `relinkFailed`, or the refusal's `message` with a pointer to the task board. A record carrying no `relinkFailed` reads as empty. `no-match` and `no-board` stay quiet.
56
- - Records push: the count of changed paths when above zero, or the refusal reason. `no-repository` stays quiet.
57
- - Reclaim: the removal count and the failure count, both read from the fields whatever the exit, or an unreadable reason. `gh-missing` stays quiet.
58
- - Upgrade and plugin update: the record's `message` on any state but `current`, or on a failure. The plugin update stays quiet on `no-claude` and `no-plugin`.
@@ -1,81 +0,0 @@
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 `.canon/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 `claude/skills/plan-intake/references/intake.md`.