@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
@@ -41,7 +41,11 @@ canon tasks next-label --claim --json
41
41
 
42
42
  The claim reserves the label under `.canon/ordinal-locks/`, so a second session allocating at the same moment gets the next one. A bare read reserves nothing and can return a label a live claim already holds.
43
43
 
44
- Branch on the record's `reason` rather than on the exit code, taking `label` from a record that carries one. `label-contended` means re-run the claim. Report the verb as unavailable and stop when the installed binary carries no `next-label` subcommand, or when an unknown `--claim` prints no record, since that binary predates the flag. Never fall back to a bare read or a label picked by hand, which reopens the collision the claim exists to prevent.
44
+ Branch on the record's `reason` rather than on the exit code, taking `label` from a record that carries one.
45
+
46
+ - `label-contended` means re-run the claim
47
+ - Report the verb as unavailable and stop when the binary carries no `next-label` subcommand, or prints no record for an unknown `--claim`, since it predates the flag
48
+ - Never fall back to a bare read or a label picked by hand, which reopens the collision the claim exists to prevent
45
49
 
46
50
  ## Step 3: write the overview
47
51
 
@@ -71,7 +75,9 @@ Run the match check in `${CLAUDE_SKILL_DIR}/references/assembly.md` before going
71
75
 
72
76
  ## Step 6: write the task
73
77
 
74
- Write `<main-root>/.canon/tasks/<label>-<slug>.md` by heredoc, from the label Step 2 claimed, without calling the verb again, since a second call would claim a different label. Its frontmatter, H1, and `## Outcomes` follow `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md`. Write a `Ready:` line beside the `Plan:` line pointing at the folder, then point the task at the plan through the verb:
78
+ Write `<main-root>/.canon/tasks/<label>-<slug>.md` by heredoc from the label Step 2 claimed, never calling the verb again, which would claim another. Its frontmatter, H1, and `## Outcomes` follow `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md`.
79
+
80
+ Write a `Ready:` line beside the `Plan:` line pointing at the folder, then point the task at the plan through the verb:
75
81
 
76
82
  ```bash
77
83
  canon tasks plan-link <task-stem> .canon/plans/feature-<slug>.md --json
@@ -79,13 +85,19 @@ canon tasks plan-link <task-stem> .canon/plans/feature-<slug>.md --json
79
85
 
80
86
  Branch on the record rather than on the exit. The `Ready:` line orients a worker reading the task, and `canon tasks archive` reads it to move the folder on ship.
81
87
 
82
- A task file with no row is a dropped task, so place the row in the same pass. The plan exists, so the row takes `## Run now`, per the tests in `${CLAUDE_SKILL_DIR}/../task-board/references/board.md`. A path `canon tasks plan-reach` reports as claimed is a shared file rather than a hold. Name each one and its holder wherever the row is reported below, so whoever controls the board decides whether a contract, a relocation, or a sweep moves the row to `## Up next`.
88
+ ### Place the row
89
+
90
+ A task file with no row is a dropped task, so place the row in the same pass. The plan exists, so the row takes `## Run now`, per the tests in `${CLAUDE_SKILL_DIR}/../task-board/references/board.md`.
91
+
92
+ A path `canon tasks plan-reach` reports as claimed is a shared file rather than a hold. Name each one and its holder wherever the row is reported, so whoever controls the board decides whether a contract, a relocation, or a sweep moves the row to `## Up next`.
93
+
94
+ ### Check the roster
83
95
 
84
- Check the roster the way `canon:task-board` Step 4 does, reading `canon sessions list --self --json` for this session and `canon sessions list --json` for a row from the same repository, under another `sessionId`, whose `name` starts with `orchestrator-`. Treat a refused read as a roster read that failed.
96
+ Check the roster the way `canon:task-board` Step 4 does, reading `canon sessions list --self --json` for this session and `canon sessions list --json` for a same-repository row under another `sessionId` whose `name` starts with `orchestrator-`. A refused read counts as failed.
85
97
 
86
- - **Orchestrator found.** Write neither file. Message that session with the row's title, the plan link, and the group with its reason, so it places the row itself rather than two sessions writing the board at once.
87
- - **Not found, and this session's name starts with `worker-` or `planner-`.** Write neither file, since both role bodies ban a board write with no exception. Report the row and its group in Step 8.
88
- - **Not found otherwise, or the roster read fails.** Write the row into `priority.md` through the same `Bash` route the task file took. A failed read looks like a solo project, and stopping would strand the task on the one path with nobody to place it.
98
+ - **Orchestrator found.** Write neither file. Message that session with the row's title, the plan link, and the group with its reason, so two sessions never write the board at once.
99
+ - **Not found, and this session's name starts with `worker-` or `planner-`.** Write neither file, since both role bodies ban a board write. Report the row and its group in Step 8.
100
+ - **Not found otherwise, or the roster read fails.** Write the row into `priority.md` through the task file's `Bash` route, since a failed read looks like a solo project and stopping strands the task.
89
101
 
90
102
  Say which branch fired in Step 8.
91
103
 
@@ -10,8 +10,9 @@ metadata:
10
10
  ## Guards
11
11
 
12
12
  - If no topic is provided, stop: `❌ No screencast topic. Describe what you are recording.`
13
- - Draft, then hand off. Do not edit video or generate captions, and do not drive the application. A recording is another skill's job rather than something forbidden: write the draft, name `record-screencast` as the next step, and stop.
14
- - Stack-agnostic in the draft. Never name a recording tool, an editing tool, a font, or a window manager. Keep selectors, URLs, wait conditions, and timings out of the beats too, since those four are exactly what the compiler adds in a plan of its own. A beat carrying them stops being a document a person can read and edit down.
13
+ - Draft, then hand off. Do not edit video, generate captions, or drive the application, since a recording is `record-screencast`'s job. Name it as the next step and stop.
14
+ - Stack-agnostic in the draft. Never name a recording tool, an editing tool, a font, or a window manager.
15
+ - Keep selectors, URLs, wait conditions, and timings out of the beats, since the compiler adds those four in a plan of its own and a beat carrying them stops being readable
15
16
 
16
17
  ## Step 1: read the project context
17
18
 
@@ -45,118 +46,11 @@ Build a 2-to-4-word kebab-case slug from the topic and discovery answers. Exampl
45
46
 
46
47
  Create `demos/<slug>/beats.md` at the project root, creating the folder if it does not exist. The file is committed with the rest of the demo's sources, so a recording can be rebuilt from the folder alone.
47
48
 
48
- Write all nine sections. Pre-seed every section with concrete content so the draft is shippable as-is. The user edits down rather than fills blanks.
49
+ ### Fill the template
49
50
 
50
- Load `canon:video-craft` for every zoom and caption value the draft names, and report it rather than proceeding silently when it does not resolve. The template below states none, since each number there traces to a source that skill records.
51
+ Write all nine sections from `${CLAUDE_SKILL_DIR}/references/beats-template.md`, read on reaching this step. Pre-seed every section with concrete content so the draft ships as-is, and the user edits down rather than fills blanks. A run stopped at discovery skips it.
51
52
 
52
- ```markdown
53
- # Screencast: <short title>
54
-
55
- ## 1. Header
56
-
57
- - Audience: <from discovery>
58
- - Length: <from discovery>
59
- - Hero moment: <from discovery>
60
- - Pitch: <one sentence, derived from project context>
61
-
62
- ## 2. Pre-recording checklist
63
-
64
- - [ ] Browser at 1920x1080 with notifications silenced
65
- - [ ] Session storage and history cleared for the recorded surfaces
66
- - [ ] Cursor highlighter on
67
- - [ ] Live URLs and fixtures verified healthy
68
- - [ ] <one or two project-specific items derived from context>
69
-
70
- ## 3. Beat sheet
71
-
72
- Five beats. Each uses five fields. Add a sixth `Transition out` only when non-default.
73
-
74
- ### Beat 1: Cold open
75
-
76
- - On screen: <derived from hero moment>
77
- - Action: <one verb, derived>
78
- - Watch for:
79
- - Emphasis: zoom onto the action, scale and speed per `canon:video-craft`
80
- - Caption:
81
-
82
- ### Beat 2: Setup
83
-
84
- - On screen:
85
- - Action:
86
- - Watch for:
87
- - Emphasis: none
88
- - Caption:
89
-
90
- ### Beat 3: Hero moment
91
-
92
- - On screen: <hero moment from discovery>
93
- - Action:
94
- - Watch for:
95
- - Emphasis: highlight overlay
96
- - Caption:
97
-
98
- ### Beat 4: Payoff
99
-
100
- - On screen:
101
- - Action:
102
- - Watch for:
103
- - Emphasis: none
104
- - Caption:
105
-
106
- ### Beat 5: Outro
107
-
108
- - On screen:
109
- - Action:
110
- - Watch for:
111
- - Emphasis: zoom out to full frame
112
- - Caption:
113
-
114
- ## 4. Sequencing notes
115
-
116
- - Hover before click. The cursor needs a beat to register.
117
- - Do not cut mid-reset. Let state changes complete on screen.
118
- - Cut on action, not on stillness.
119
- - <one or two project-specific gotchas derived from context>
120
-
121
- ## 5. Production notes
122
-
123
- - Resolution: 1920x1080
124
- - Frame rate: 30fps
125
- - Container: mp4 H.264
126
- - Zoom: only on an action too small to read at full frame, scale, speed, and dwell per `canon:video-craft`, zoomed out before every cut
127
- - Highlight overlay: ~1.5s soft glow
128
-
129
- ## 6. Captions
130
-
131
- - Each caption names what its beat's frame shows, at most two lines of 42 characters, per `canon:video-craft`
132
- - Hold and reading rate per `canon:video-craft`, splitting a caption across beats rather than shortening its hold
133
- - Placed off the element the beat acts on
134
-
135
- ## 7. Distribution
136
-
137
- Where the recording ships. Strike rows that do not apply.
138
-
139
- - [ ] Native upload to social platform
140
- - [ ] Canonical host (long-form video platform)
141
- - [ ] Linked from project README or release notes
142
-
143
- ## 8. Resolved decisions
144
-
145
- Pre-seeded with the common picks. Strike or rewrite as needed.
146
-
147
- - Audio: silent plus captions
148
- - Language: English
149
- - Chip order: <leave for user, derived from beats>
150
- - Thumbnail: hero-moment frame
151
-
152
- ## 9. Wrap
153
-
154
- What surrounds the take in the finished video. Each row carries a proposed default. Write `none` on every row to ship the raw take.
155
-
156
- - Intro: <proposed title card of two to three seconds naming the subject, or `none`>
157
- - Outro: <proposed closing card naming where to go next, or `none`>
158
- - Music: none
159
- ```
53
+ Load `canon:video-craft` for every zoom and caption value the draft names, and report it rather than proceeding silently when it does not resolve.
160
54
 
161
55
  ## Step 5: output
162
56
 
@@ -0,0 +1,119 @@
1
+ ---
2
+ title: Beats template
3
+ description: The nine-section template draft-screencast writes to demos/<slug>/beats.md, pre-seeded so the draft ships as-is
4
+ ---
5
+
6
+ # Beats template
7
+
8
+ Applies to the draft `draft-screencast` Step 4 writes. Fill every placeholder from the project context and the discovery answers. The template states no zoom or caption value, since each traces to a source `canon:video-craft` records.
9
+
10
+ ## Template
11
+
12
+ ```markdown
13
+ # Screencast: <short title>
14
+
15
+ ## 1. Header
16
+
17
+ - Audience: <from discovery>
18
+ - Length: <from discovery>
19
+ - Hero moment: <from discovery>
20
+ - Pitch: <one sentence, derived from project context>
21
+
22
+ ## 2. Pre-recording checklist
23
+
24
+ - [ ] Browser at 1920x1080 with notifications silenced
25
+ - [ ] Session storage and history cleared for the recorded surfaces
26
+ - [ ] Cursor highlighter on
27
+ - [ ] Live URLs and fixtures verified healthy
28
+ - [ ] <one or two project-specific items derived from context>
29
+
30
+ ## 3. Beat sheet
31
+
32
+ Five beats. Each uses five fields. Add a sixth `Transition out` only when non-default.
33
+
34
+ ### Beat 1: Cold open
35
+
36
+ - On screen: <derived from hero moment>
37
+ - Action: <one verb, derived>
38
+ - Watch for:
39
+ - Emphasis: zoom onto the action, scale and speed per `canon:video-craft`
40
+ - Caption:
41
+
42
+ ### Beat 2: Setup
43
+
44
+ - On screen:
45
+ - Action:
46
+ - Watch for:
47
+ - Emphasis: none
48
+ - Caption:
49
+
50
+ ### Beat 3: Hero moment
51
+
52
+ - On screen: <hero moment from discovery>
53
+ - Action:
54
+ - Watch for:
55
+ - Emphasis: highlight overlay
56
+ - Caption:
57
+
58
+ ### Beat 4: Payoff
59
+
60
+ - On screen:
61
+ - Action:
62
+ - Watch for:
63
+ - Emphasis: none
64
+ - Caption:
65
+
66
+ ### Beat 5: Outro
67
+
68
+ - On screen:
69
+ - Action:
70
+ - Watch for:
71
+ - Emphasis: zoom out to full frame
72
+ - Caption:
73
+
74
+ ## 4. Sequencing notes
75
+
76
+ - Hover before click. The cursor needs a beat to register.
77
+ - Do not cut mid-reset. Let state changes complete on screen.
78
+ - Cut on action, not on stillness.
79
+ - <one or two project-specific gotchas derived from context>
80
+
81
+ ## 5. Production notes
82
+
83
+ - Resolution: 1920x1080
84
+ - Frame rate: 30fps
85
+ - Container: mp4 H.264
86
+ - Zoom: only on an action too small to read at full frame, scale, speed, and dwell per `canon:video-craft`, zoomed out before every cut
87
+ - Highlight overlay: ~1.5s soft glow
88
+
89
+ ## 6. Captions
90
+
91
+ - Each caption names what its beat's frame shows, at most two lines of 42 characters, per `canon:video-craft`
92
+ - Hold and reading rate per `canon:video-craft`, splitting a caption across beats rather than shortening its hold
93
+ - Placed off the element the beat acts on
94
+
95
+ ## 7. Distribution
96
+
97
+ Where the recording ships. Strike rows that do not apply.
98
+
99
+ - [ ] Native upload to social platform
100
+ - [ ] Canonical host (long-form video platform)
101
+ - [ ] Linked from project README or release notes
102
+
103
+ ## 8. Resolved decisions
104
+
105
+ Pre-seeded with the common picks. Strike or rewrite as needed.
106
+
107
+ - Audio: silent plus captions
108
+ - Language: English
109
+ - Chip order: <leave for user, derived from beats>
110
+ - Thumbnail: hero-moment frame
111
+
112
+ ## 9. Wrap
113
+
114
+ What surrounds the take in the finished video. Each row carries a proposed default. Write `none` on every row to ship the raw take.
115
+
116
+ - Intro: <proposed title card of two to three seconds naming the subject, or `none`>
117
+ - Outro: <proposed closing card naming where to go next, or `none`>
118
+ - Music: none
119
+ ```
@@ -11,16 +11,15 @@ Draw each slide as an HTML file in a deck folder, then shell out to `canon slide
11
11
 
12
12
  ## Read the decks and the format
13
13
 
14
- Run both first. Never hardcode a deck name, an attribute, or a chart type. The CLI is the source of truth for each.
14
+ Run the list first, then read the format. Never hardcode a deck name, an attribute, or a chart type.
15
15
 
16
16
  ```bash
17
17
  canon slides list --json 2>/dev/null
18
- canon docs slides
19
18
  ```
20
19
 
21
20
  The list returns one row per deck, carrying its `name`, `title`, slide count, and `path`. When the request names a deck the list holds, edit that folder. Otherwise pick a new kebab-case name no row carries.
22
21
 
23
- The docs page states what `deck.json` holds, what each slide declares on its `<body>`, the motion attributes, speaker notes, charts, and what each refusal reports.
22
+ Read `${CLAUDE_SKILL_DIR}/references/deck.md`. It states what `deck.json` holds, what each slide declares on its `<body>`, the motion attributes, speaker notes, and charts. Each refusal prints as a `✗` line from the render.
24
23
 
25
24
  ## Draw the deck
26
25
 
@@ -38,15 +37,18 @@ Each slide is a full HTML document laid out at 1280 by 720 CSS pixels. Position
38
37
 
39
38
  The render injects the project's token stylesheet ahead of each slide's own styles, so the deck takes the project's design rather than the toolkit's.
40
39
 
41
- - Use the project's custom properties, such as `var(--color-accent)`, for every color, face, and spacing value the stylesheet defines. Read the names from `.claude/design/base.css` and any overrides under `.claude/design/project/`. When neither exists, say the deck renders unstyled rather than inventing values to cover it.
42
- - Leave the `--color-*` roles to the stylesheet rather than redefining them in a slide. The master reads its colors off the first slide's `<html>`, and a slide body computing its own `--color-text` is reported, since the bands keep the master's colors.
40
+ - Use the project's custom properties, such as `var(--color-accent)`, for every color, face, and spacing value, read from `.claude/design/base.css` and overrides under `.claude/design/project/`
41
+ - When neither exists, say the deck renders unstyled rather than inventing values to cover it
42
+ - Leave the `--color-*` roles to the stylesheet. The master reads its colors off the first slide's `<html>`, so a slide computing its own `--color-text` is reported, since the bands keep the master's.
43
43
  - Vary the composition across slides. Never repeat a title over bullets on every slide. Reach for columns, a row of large numbers, a grid of cards, a chart, a pull quote, and section dividers.
44
44
 
45
45
  ### Fit and shapes
46
46
 
47
+ Text and shapes have to survive the conversion.
48
+
47
49
  - Keep titles short and lines tight, so text fits its box at the size the design sets rather than overflowing the frame.
48
50
  - Never write centered body text.
49
- - Prefer CSS the converter maps to native shapes. An element computing a property it cannot map is drawn as a picture of itself and reported on stderr, so a slide full of gradients and filters stops being editable.
51
+ - Prefer CSS the converter maps to native shapes. An unmappable property is drawn as a picture and reported on stderr, so a slide full of gradients and filters stops being editable.
50
52
 
51
53
  ## Render
52
54
 
@@ -64,7 +66,7 @@ Read every `✗` line the render prints. A picture fallback, a refused link, cha
64
66
 
65
67
  Run this once after the first render, then stop.
66
68
 
67
- 1. Convert the deck to images: `soffice --headless --convert-to pdf <deck>.pptx` then `pdftoppm -r 90 -png <deck>.pdf slide`. If `soffice` or `pdftoppm` is missing, skip the image pass and say so. Do not fail the render.
69
+ 1. Convert the deck with `soffice --headless --convert-to pdf <deck>.pptx`, then `pdftoppm -r 90 -png <deck>.pdf slide`. If either is missing, skip the image pass and say so rather than fail the render.
68
70
  2. Open and inspect every rendered image in this session, checking each slide for overlap, overflow, low contrast, and empty regions.
69
71
  3. Fix the reported issues and the `✗` lines in the slide files once, re-render, and stop. Do not loop indefinitely on aesthetics.
70
72
 
@@ -0,0 +1,183 @@
1
+ ---
2
+ title: Deck reference
3
+ description: The deck folder, the deck.json fields, the attributes a slide sets on its body, motion, the master's token roles, speaker notes, and charts
4
+ ---
5
+
6
+ # Deck reference
7
+
8
+ Applies to the slides under `.canon/slides/<deck>/`, which `canon slides render <deck>` turns into an editable PowerPoint file. Each slide describes its own shapes. What belongs to the whole deck sits in `deck.json`.
9
+
10
+ ## Contents
11
+
12
+ - [The deck folder](#the-deck-folder)
13
+ - [The deck file](#the-deck-file)
14
+ - [What each slide declares](#what-each-slide-declares)
15
+ - [Motion](#motion)
16
+ - [The master](#the-master)
17
+ - [Speaker notes](#speaker-notes)
18
+ - [Charts](#charts)
19
+
20
+ ## The deck folder
21
+
22
+ | Path | Holds |
23
+ | -------------------------------- | ---------------------------------------------------- |
24
+ | `.canon/slides/<deck>/*.html` | the slides, rendered in filename order |
25
+ | `.canon/slides/<deck>/deck.json` | what belongs to the whole deck |
26
+ | `.canon/slides/<deck>/assets/` | images the slides reference by a relative path |
27
+ | `.canon/slides/layouts/` | the project's shared layouts, never listed as a deck |
28
+
29
+ A folder holding no `.html` file is not a deck. `render` with no name takes the only deck, and refuses with the list when there are several. It also takes a folder path outside `.canon/slides/`.
30
+
31
+ ## The deck file
32
+
33
+ `deck.json` is optional. Without it the deck takes the defaults below.
34
+
35
+ | Field | Default | What it sets |
36
+ | -------------- | ------------------------------ | ------------------------------------------------- |
37
+ | `title` | the folder name | the file's title and the default footer text |
38
+ | `header` | off | a band at the top, with `left`, `center`, `right` |
39
+ | `footer` | on, with the title on the left | a band at the bottom, with the same three slots |
40
+ | `slideNumbers` | `true` | the number at the right of the footer |
41
+ | `mark` | none | an image path, relative to the folder, top right |
42
+ | `fonts` | none | faces to embed in the file |
43
+
44
+ ```json
45
+ {
46
+ "title": "Q3 review",
47
+ "header": { "left": "Acme" },
48
+ "footer": { "center": "Internal" },
49
+ "mark": "assets/mark.png",
50
+ "fonts": [
51
+ { "family": "Inter", "path": "fonts/Inter-Regular.ttf" },
52
+ { "family": "Inter", "weight": 700, "path": "fonts/Inter-Bold.ttf" }
53
+ ]
54
+ }
55
+ ```
56
+
57
+ ### Bands and the mark
58
+
59
+ A band takes `show` and the three slots. A declared band merges over its default, so `"footer": { "center": "Internal" }` keeps the title on the left. Declaring any slot turns a band on unless it also says `"show": false`. Set a slot to `""` to empty it.
60
+
61
+ - Slide numbers hold the footer's right slot and show only where the footer does. Set `slideNumbers` to `false` to use `footer.right`.
62
+ - Prefer a raster mark, since an SVG mark lands as a vector with a broken fallback.
63
+
64
+ ### Embedded faces
65
+
66
+ Each `fonts` entry names one TrueType or OpenType file inside the deck folder:
67
+
68
+ | Key | Default | Rule |
69
+ | -------- | -------- | ------------------------------------------------------------------- |
70
+ | `family` | required | match the family the slides set |
71
+ | `weight` | `400` | `600` and above fills the bold slot, anything lighter fills regular |
72
+ | `style` | `normal` | `normal` or `italic` |
73
+ | `path` | required | relative to the folder, and has to stay inside it |
74
+
75
+ A face is left out, and the deck still written, when its license forbids embedding, when it is a variable font or a `woff` or `woff2` file, or when a second face lands in a filled slot. Supply static instances, one per weight, and the TrueType or OpenType file a `woff2` was compressed from.
76
+
77
+ ## What each slide declares
78
+
79
+ A slide sets its place in the deck through attributes on `<body>`.
80
+
81
+ | Attribute | Effect |
82
+ | --------------------------------------- | --------------------------------------------- |
83
+ | `data-section="Results"` | opens a section the slides after it stay in |
84
+ | `data-hidden` | hides the slide in the show |
85
+ | `data-header="off"` | drops the header on this slide |
86
+ | `data-footer="off"` | drops the footer and its number on this slide |
87
+ | `data-footer-left`, `-center`, `-right` | replaces that footer slot on this slide alone |
88
+ | `data-header-left`, `-center`, `-right` | replaces that header slot on this slide alone |
89
+
90
+ Once any slide opens a section, slides ahead of the first sit in a section named after the deck. A title the deck returns to after another section opens a new section numbered `Intro (2)`.
91
+
92
+ A slide dropping one band keeps the other from the master. A slide replacing a slot draws its band as its own text, so a later edit in the master view skips it.
93
+
94
+ ## Motion
95
+
96
+ | Attribute | Takes | Effect |
97
+ | -------------------------- | ------------------------------- | -------------------------------------- |
98
+ | `data-transition` | `fade`, `push`, `wipe`, `cover` | the transition into this slide |
99
+ | `data-transition-duration` | `500`, `500ms`, or `0.5s` | rounded to 0.5, 0.75, or 1 second |
100
+ | `data-enter` | `fade`, `fly`, `wipe`, `zoom` | an entrance played on a click |
101
+ | `data-enter-order` | a number | the click it plays on, lowest first |
102
+ | `data-enter-duration` | `500`, `500ms`, or `0.5s` | the entrance length, 500 ms by default |
103
+
104
+ `data-transition` and `data-transition-duration` go on `<body>`. The `data-enter` family goes on the element.
105
+
106
+ ```html
107
+ <body data-transition="fade">
108
+ <h1>Results</h1>
109
+ <div class="card" data-enter="fly" data-enter-order="1">…</div>
110
+ <div class="card" data-enter="fly" data-enter-order="2">…</div>
111
+ </body>
112
+ ```
113
+
114
+ - Each entrance is its own click, and entrances without an order play after the ordered ones, in page order.
115
+ - An entrance brings in every shape drawn from its element and the elements inside it, so a card's box and text arrive together.
116
+ - An inner element with its own `data-enter` waits for its own click.
117
+ - Fly comes in from the bottom and wipe from the left.
118
+
119
+ ## The master
120
+
121
+ The master takes its colors from the `<html>` element of the first slide, laid out with the project's token stylesheet injected:
122
+
123
+ | Source | Used for |
124
+ | -------------------------------------------- | ---------------- |
125
+ | `--color-background` | the slide ground |
126
+ | `--color-text` | the header text |
127
+ | `--color-muted` | the footer text |
128
+ | `--color-accent` | the slide number |
129
+ | `<body>` font family, ` Variable` suffix cut | the theme face |
130
+
131
+ A role the project declares no token for falls back to the slide body's own color and prints one `✗` line naming it. It never falls back to the toolkit's palette.
132
+
133
+ A slide whose `<body>` background differs from the master's keeps its own. A slide whose `<body>` computes a `--color-text` apart from the master's prints a `✗` line, since its bands keep the master's colors and may not read against its ground.
134
+
135
+ ## Speaker notes
136
+
137
+ `<aside class="notes">` becomes the slide's notes and is never drawn. Each paragraph or list item becomes its own line, and markup is dropped.
138
+
139
+ ## Charts
140
+
141
+ A `<figure data-chart="...">` holding a `<table>` becomes a native, editable chart where the figure was laid out. The table is not drawn as well.
142
+
143
+ ```html
144
+ <figure data-chart="bar" data-labels>
145
+ <figcaption>Revenue by region</figcaption>
146
+ <table>
147
+ <tr>
148
+ <th>Region</th>
149
+ <th>Q1</th>
150
+ <th>Q2</th>
151
+ </tr>
152
+ <tr>
153
+ <td>North</td>
154
+ <td>12</td>
155
+ <td>16</td>
156
+ </tr>
157
+ <tr>
158
+ <td>South</td>
159
+ <td></td>
160
+ <td>11</td>
161
+ </tr>
162
+ </table>
163
+ </figure>
164
+ ```
165
+
166
+ - `data-chart` takes `bar`, `line`, `area`, `pie`, or `doughnut`.
167
+ - The header row names the series and the first column names the categories.
168
+ - An empty cell is a gap, not a zero. A comma is a thousands separator only where it groups in threes, as in `12,345.5`, so a comma-decimal `3,5` refuses.
169
+ - `data-labels` shows each value, and a `<figcaption>` becomes the chart title.
170
+ - Series take the accent first, then every other declared `--color-*` role in sheet order except the background and surface, repeating past the last.
171
+
172
+ A refused chart, effect, or face is left out and the rest of the deck is written. Each prints a `✗` line, and the exit depends on the case:
173
+
174
+ | Case | Exit |
175
+ | ----------------------------------------------------------------------- | ---- |
176
+ | malformed or unknown `deck.json` field | 1 |
177
+ | `mark` path missing | 1 |
178
+ | `fonts` path missing or outside the folder | 1 |
179
+ | face that cannot embed | 0 |
180
+ | unknown transition or entrance name | 0 |
181
+ | entrance on an element that draws nothing | 0 |
182
+ | chart cell that is not a number, or `data-chart` off-list | 0 |
183
+ | token role not declared, or a slide body setting its own `--color-text` | 0 |
@@ -31,6 +31,8 @@ Run these commands in parallel to gather git context:
31
31
 
32
32
  ### Preview
33
33
 
34
+ Output the preview first.
35
+
34
36
  - **Current:** <current_branch>
35
37
  - **Suggested:** <suggested_name>
36
38
  - **Length:** <count>/50
@@ -55,8 +57,6 @@ git branch -m <current> <suggested>
55
57
 
56
58
  ## After execution
57
59
 
58
- Respond with exactly one line:
60
+ Respond with exactly one line and add no other text:
59
61
 
60
62
  `✅ Renamed: <current> → <suggested>`
61
-
62
- Do not add any other text.
@@ -30,6 +30,8 @@ Run these commands in parallel to gather git context:
30
30
 
31
31
  ### Preview
32
32
 
33
+ Output the preview first.
34
+
33
35
  - **Files:** <if ≤3 list all, if >3 show first 3 + "+N more">
34
36
  - **Message:** `<type>(<scope>): <subject>`
35
37
  - **Length:** <count>/72
@@ -38,14 +40,14 @@ After outputting the preview, execute the final command immediately. Claude Code
38
40
 
39
41
  ### Final command
40
42
 
43
+ Run it as soon as the preview is out.
44
+
41
45
  ```bash
42
46
  git commit -m "<type>(<scope>): <subject>"
43
47
  ```
44
48
 
45
49
  ## After execution
46
50
 
47
- Respond with exactly one line:
51
+ Respond with exactly one line and add no other text:
48
52
 
49
53
  `✅ Committed: <type>(<scope>): <subject>`
50
-
51
- Do not add any other text.
@@ -16,18 +16,25 @@ its own reply, so skip the comment in step 7. The push and body sync still run.
16
16
 
17
17
  - If `git branch --show-current` returns `main`, stop: `❌ On main. Switch to a PR branch first.`
18
18
  - If `git status --porcelain` is empty, stop: `❌ No changes to ship.`
19
- - Resolve `<number>` once, here, through `canon pr head --json`, reading the record's `number`. Every later step names this `<number>` rather than resolving it again. A record refusing with `ambiguous-pull` stops: `❌ Two open PRs on this branch. Name the one to update.`
20
- - When no record comes back, or it carries no `number`, read the open pull requests on the branch over REST instead. The branch is encoded so a `/` in its name reaches the query as `%2F`:
19
+ - Resolve `<number>` once, here, through `canon pr head --json`, reading the record's `number`. Every later step names this `<number>` rather than resolving it again.
20
+ - A record refusing with `ambiguous-pull` stops: `❌ Two open PRs on this branch. Name the one to update.`
21
21
 
22
- ```bash
23
- gh api "repos/{owner}/{repo}/pulls?head={owner}:$(git branch --show-current | jq -Rr @uri)&state=open" --jq '.[].number'
24
- ```
22
+ ### Fallback lookup
25
23
 
26
- Nothing printed stops: `❌ No open PR for this branch. Use git-ship to open one.` More than one number takes the `ambiguous-pull` stop above rather than the first, since two open pull requests on one head against different bases leave nothing to say which one this push updates.
24
+ When no record comes back, or it carries no `number`, read the open pull requests on the branch over REST instead. The branch is encoded so a `/` in its name reaches the query as `%2F`:
27
25
 
28
- Every read and write in this skill runs on REST, since the `gh pr` subcommands run on GraphQL and a cloud session's GitHub proxy refuses it. A cloud worker answering a review reaches this skill through `review-address`, so one GraphQL call here breaks that whole leg.
26
+ ```bash
27
+ gh api "repos/{owner}/{repo}/pulls?head={owner}:$(git branch --show-current | jq -Rr @uri)&state=open" --jq '.[].number'
28
+ ```
29
+
30
+ - Nothing printed stops: `❌ No open PR for this branch. Use git-ship to open one.`
31
+ - More than one number takes the `ambiguous-pull` stop rather than the first, since two open pull requests on one head leave nothing to say which one this push updates
32
+
33
+ ### REST and the tracking ref
34
+
35
+ Every read and write in this skill runs on REST, since the `gh pr` subcommands run on GraphQL and a cloud session's GitHub proxy refuses it. A cloud worker reaches this skill through `review-address`, so one GraphQL call breaks that whole leg.
29
36
 
30
- A missing tracking ref is no longer a guard. An open pull request proves the branch reached the remote, and a branch created by worktree entry has been measured carrying an open pull request with no tracking ref, where the old guard reported that the branch still needed pushing. That message describes a state the branch is not in, so the run stopped on a diagnosis nobody could act on. Step 4 sets the ref instead.
37
+ A missing tracking ref is not a guard, since an open pull request proves the branch reached the remote. A branch created by worktree entry carries an open pull request with no tracking ref, so step 4 sets the ref instead.
31
38
 
32
39
  ## Sequence
33
40
 
@@ -40,7 +47,7 @@ A missing tracking ref is no longer a guard. An open pull request proves the bra
40
47
 
41
48
  The lease is what stops the force from overwriting a commit this session never read. Run the ancestry test only where an upstream resolves, since it reads `@{u}`.
42
49
 
43
- 5. Post the evidence comparison, on every invocation including `reply-owned`, since this comment is not the reply step 8 owns. Run `canon pr evidence <number> --json` and read `reason` on the record rather than the exit code.
50
+ 5. Post the evidence comparison on every invocation, `reply-owned` included, since it is not step 8's reply. Run `canon pr evidence <number> --json` and read `reason` rather than the exit code.
44
51
  - `no-evidence`: nothing changed under an `evidence/` segment this push. Say nothing and move on.
45
52
  - `ok`: write `body` to `.canon/tmp/pr/evidence/body-<number>.md` at the main worktree root (resolved the way `session-worktree` does), then post the comment with `gh api -X POST repos/{owner}/{repo}/issues/<number>/comments -F body=@<main-root>/.canon/tmp/pr/evidence/body-<number>.md --silent` when the record carries no `commentId`, or edit the existing one in place with `gh api -X PATCH repos/{owner}/{repo}/issues/comments/<commentId> -F body=@<main-root>/.canon/tmp/pr/evidence/body-<number>.md` when it does. Clean up the tmp file only after the call reports success.
46
53
  - Any other reason is one of the mirrored git refusals (`gh-missing`, `gh-failed`, `no-base`, `unreadable-changes`, `would-empty`). Report it and continue without stopping the chain.