@erclx/canon 5.40.0 → 5.42.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 (187) hide show
  1. package/README.md +34 -17
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/references/adopted.md +34 -14
  4. package/claude/skills/auto-ship/REQUIREMENT.md +2 -0
  5. package/claude/skills/auto-ship/SKILL.md +28 -2
  6. package/claude/skills/auto-ship/references/draft-mark.md +32 -6
  7. package/claude/skills/auto-ship/references/failure-recovery.md +15 -13
  8. package/claude/skills/auto-ship/references/fan-in.md +59 -0
  9. package/claude/skills/auto-ship/references/tier-failures.md +31 -10
  10. package/claude/skills/auto-ship/references/ui-checklist.md +9 -4
  11. package/claude/skills/auto-ship/references/worktree-name.md +9 -3
  12. package/claude/skills/build-in-slices/references/adopted.md +35 -21
  13. package/claude/skills/canvas-board/references/canvas.md +17 -6
  14. package/claude/skills/canvas-board/references/design.md +26 -7
  15. package/claude/skills/code-craft/references/adopted.md +58 -20
  16. package/claude/skills/codebase-layout/references/adopted.md +49 -16
  17. package/claude/skills/codebase-layout/references/frontend.md +11 -4
  18. package/claude/skills/codebase-layout/references/tests.md +4 -2
  19. package/claude/skills/context-fold/references/classify.md +10 -15
  20. package/claude/skills/context-fold/references/completion-report.md +11 -9
  21. package/claude/skills/context-fold/references/context-refresh.md +26 -9
  22. package/claude/skills/context-fold/references/unusable-baseline.md +5 -3
  23. package/claude/skills/create-rule/REQUIREMENT.md +5 -0
  24. package/claude/skills/create-rule/SKILL.md +9 -1
  25. package/claude/skills/create-rule/references/checklist.md +26 -0
  26. package/claude/skills/create-rule/references/rule.md +98 -20
  27. package/claude/skills/create-skill/SKILL.md +1 -0
  28. package/claude/skills/create-skill/references/skill-disclosure.md +10 -2
  29. package/claude/skills/create-skill/references/skill-frontmatter.md +43 -0
  30. package/claude/skills/create-skill/references/skill-paths.md +7 -4
  31. package/claude/skills/create-skill/references/skill-practice.md +6 -3
  32. package/claude/skills/create-skill/references/skill-requirement.md +4 -2
  33. package/claude/skills/create-skill/references/skill.md +67 -44
  34. package/claude/skills/decision-escalate/REQUIREMENT.md +1 -1
  35. package/claude/skills/decision-escalate/SKILL.md +1 -2
  36. package/claude/skills/deploy-app/references/vercel.md +2 -1
  37. package/claude/skills/deprecation-migration/references/adopted.md +44 -25
  38. package/claude/skills/design-taste/references/adopted.md +9 -1
  39. package/claude/skills/design-taste/references/craft.md +3 -1
  40. package/claude/skills/design-taste/references/kinds.md +8 -7
  41. package/claude/skills/design-taste/references/preflight.md +12 -0
  42. package/claude/skills/design-taste/references/systems.md +7 -6
  43. package/claude/skills/design-taste/references/tells.md +24 -25
  44. package/claude/skills/design-taste/references/vocabulary.md +7 -5
  45. package/claude/skills/document-health/references/axes.md +54 -30
  46. package/claude/skills/draft-and-pick/references/live-arms.md +2 -2
  47. package/claude/skills/draft-doc/references/context.md +4 -3
  48. package/claude/skills/draft-doc/references/docs.md +6 -3
  49. package/claude/skills/draft-doc/references/readme/agent-facing.md +6 -0
  50. package/claude/skills/draft-doc/references/readme/application.md +8 -2
  51. package/claude/skills/draft-doc/references/readme/cli.md +5 -1
  52. package/claude/skills/draft-doc/references/readme/library.md +4 -0
  53. package/claude/skills/draft-doc/references/readme/plugin.md +9 -1
  54. package/claude/skills/draft-doc/references/readme.md +30 -13
  55. package/claude/skills/draft-doc/references/requirements.md +17 -4
  56. package/claude/skills/draft-doc/references/wiki-kind.md +20 -10
  57. package/claude/skills/draft-figure/references/architecture-set.md +7 -3
  58. package/claude/skills/draft-figure/references/figures.md +8 -6
  59. package/claude/skills/draft-figure/references/hand-drawn.md +3 -1
  60. package/claude/skills/draft-figure/references/mermaid-path.md +19 -9
  61. package/claude/skills/draft-figure/references/mermaid.md +10 -5
  62. package/claude/skills/draft-ready/references/ready.md +22 -15
  63. package/claude/skills/git-branch/REQUIREMENT.md +17 -6
  64. package/claude/skills/git-branch/SKILL.md +63 -7
  65. package/claude/skills/git-branch/references/branch.md +11 -1
  66. package/claude/skills/git-branch/references/slug.md +17 -5
  67. package/claude/skills/git-commit/references/commit.md +8 -0
  68. package/claude/skills/git-commit/references/versioning.md +23 -17
  69. package/claude/skills/git-followup/REQUIREMENT.md +1 -1
  70. package/claude/skills/git-followup/SKILL.md +5 -6
  71. package/claude/skills/git-issue/references/issue.md +29 -16
  72. package/claude/skills/git-pr/REQUIREMENT.md +1 -5
  73. package/claude/skills/git-pr/SKILL.md +11 -22
  74. package/claude/skills/git-pr/references/final-command.md +21 -8
  75. package/claude/skills/git-pr/references/labels.md +45 -28
  76. package/claude/skills/git-pr/references/pr.md +28 -9
  77. package/claude/skills/git-pr/references/publish.md +42 -28
  78. package/claude/skills/git-pr/references/task-number.md +20 -8
  79. package/claude/skills/git-ship/SKILL.md +1 -1
  80. package/claude/skills/plan-feature/references/batches.md +48 -1
  81. package/claude/skills/plan-feature/references/plan-lifecycle.md +10 -3
  82. package/claude/skills/plan-feature/references/plan.md +59 -20
  83. package/claude/skills/plan-groundwork/references/groundwork-spikes.md +4 -1
  84. package/claude/skills/plan-groundwork/references/groundwork.md +14 -16
  85. package/claude/skills/plan-intake/references/intake.md +29 -24
  86. package/claude/skills/plan-intake-answer/REQUIREMENT.md +1 -1
  87. package/claude/skills/plan-intake-answer/SKILL.md +1 -2
  88. package/claude/skills/pr-evidence/REQUIREMENT.md +40 -0
  89. package/claude/skills/pr-evidence/SKILL.md +53 -0
  90. package/claude/skills/pr-evidence/references/evidence.md +150 -0
  91. package/claude/skills/review-address/references/post-review-findings.md +10 -3
  92. package/claude/skills/review-address/references/rebase-conflicts.md +22 -7
  93. package/claude/skills/review-address/references/rest-reads.md +3 -1
  94. package/claude/skills/review-craft/references/adopted.md +35 -18
  95. package/claude/skills/review-craft/references/security.md +42 -7
  96. package/claude/skills/review-craft/references/ui.md +23 -8
  97. package/claude/skills/review-pr/SKILL.md +6 -6
  98. package/claude/skills/review-pr/references/body-variants.md +29 -9
  99. package/claude/skills/review-pr/references/close-out.md +20 -5
  100. package/claude/skills/review-pr/references/key-changes.md +27 -3
  101. package/claude/skills/review-pr/references/post.md +83 -31
  102. package/claude/skills/review-pr/references/scope.md +54 -19
  103. package/claude/skills/review-ui/SKILL.md +2 -0
  104. package/claude/skills/review-ui/references/close-out.md +49 -0
  105. package/claude/skills/review-ui/references/driving.md +24 -4
  106. package/claude/skills/review-ui/references/post.md +43 -13
  107. package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +67 -17
  108. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +81 -40
  109. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +52 -15
  110. package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +48 -26
  111. package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +12 -13
  112. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +72 -36
  113. package/claude/skills/role-orchestrator/references/orchestrator-output.md +12 -4
  114. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +64 -24
  115. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +80 -26
  116. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +41 -18
  117. package/claude/skills/role-orchestrator/references/orchestrator-resume.md +19 -7
  118. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +22 -12
  119. package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +15 -6
  120. package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +25 -7
  121. package/claude/skills/role-orchestrator/references/orchestrator-watch.md +34 -8
  122. package/claude/skills/role-orchestrator/scripts/poll.ts +16 -24
  123. package/claude/skills/role-worker/REQUIREMENT.md +2 -0
  124. package/claude/skills/role-worker/SKILL.md +17 -0
  125. package/claude/skills/search-craft/references/adopted.md +18 -6
  126. package/claude/skills/session-map/references/session.md +26 -7
  127. package/claude/skills/session-worktree/references/main-root-writes.md +25 -8
  128. package/claude/skills/systematic-debugging/references/adopted.md +30 -16
  129. package/claude/skills/target-check/references/domains.md +39 -11
  130. package/claude/skills/target-setup/references/detect.md +80 -65
  131. package/claude/skills/target-setup/references/indexes.md +8 -6
  132. package/claude/skills/task-board/references/board.md +86 -30
  133. package/claude/skills/task-board/references/decline.md +29 -13
  134. package/claude/skills/task-board/references/tasks-decline.md +12 -5
  135. package/claude/skills/task-board/references/tasks.md +65 -33
  136. package/claude/skills/task-complete/references/archive.md +41 -17
  137. package/claude/skills/task-complete/references/receipt-sweep.md +23 -9
  138. package/claude/skills/task-complete/references/unusable-baseline.md +10 -3
  139. package/claude/skills/teach-workspace/REQUIREMENT.md +1 -1
  140. package/claude/skills/teach-workspace/SKILL.md +1 -2
  141. package/claude/skills/teach-workspace/references/glossary.md +6 -2
  142. package/claude/skills/teach-workspace/references/lesson-build.md +44 -16
  143. package/claude/skills/teach-workspace/references/lesson-craft.md +12 -7
  144. package/claude/skills/teach-workspace/references/pedagogy.md +6 -3
  145. package/claude/skills/teach-workspace/references/promotion-fold.md +17 -11
  146. package/claude/skills/teach-workspace/references/promotion.md +31 -8
  147. package/claude/skills/teach-workspace/references/revisit.md +8 -2
  148. package/claude/skills/teach-workspace/references/teach.md +17 -9
  149. package/claude/skills/test-craft/references/adopted.md +48 -14
  150. package/claude/skills/test-craft/references/component.md +7 -4
  151. package/claude/skills/test-craft/references/e2e.md +25 -7
  152. package/claude/skills/test-craft/references/unit.md +3 -1
  153. package/claude/skills/test-first/references/adopted.md +12 -4
  154. package/claude/skills/ui-checklist/REQUIREMENT.md +1 -1
  155. package/claude/skills/ui-checklist/SKILL.md +1 -1
  156. package/claude/skills/video-craft/references/adopted.md +62 -18
  157. package/claude/skills/write-human/references/adopted.md +21 -8
  158. package/docs/workflow/parallel-features.md +2 -1
  159. package/docs/workflow/skill-map.md +2 -1
  160. package/package.json +1 -1
  161. package/src/claude/cases/git.ts +4 -0
  162. package/src/claude/cases/workflow.ts +5 -0
  163. package/src/commands/pr.ts +174 -37
  164. package/src/commands/records.ts +11 -3
  165. package/src/context/shape-gated.ts +1 -0
  166. package/src/markdown/scope.ts +150 -2
  167. package/src/pr/frames.ts +53 -9
  168. package/src/pr/lift.ts +6 -3
  169. package/src/pr/review-state.ts +182 -0
  170. package/src/records/validate.ts +1 -0
  171. package/src/tasks/answers.ts +79 -2
  172. package/src/tasks/fan-in.ts +176 -0
  173. package/src/tasks/stack.ts +1 -1
  174. package/tooling/astro/reference.md +80 -39
  175. package/tooling/base/reference.md +63 -32
  176. package/tooling/base/seeds/canon/context/ci.md +7 -15
  177. package/tooling/base/seeds/canon/context/development.md +14 -14
  178. package/tooling/claude/reference.md +40 -22
  179. package/tooling/cloudflare/reference.md +31 -12
  180. package/tooling/go/reference.md +47 -23
  181. package/tooling/nextjs/reference.md +52 -19
  182. package/tooling/php/reference.md +35 -16
  183. package/tooling/python/reference.md +47 -30
  184. package/tooling/shell/reference.md +6 -5
  185. package/tooling/vite-react/reference.md +49 -21
  186. package/tooling/web/reference.md +130 -70
  187. package/claude/skills/git-pr/references/evidence.md +0 -124
@@ -45,6 +45,8 @@ Does not govern:
45
45
 
46
46
  ### What a scope may not be
47
47
 
48
+ A scope names a component, never a file or the type again.
49
+
48
50
  - Do not use specific filenames as scopes
49
51
  - Do not use a scope that duplicates the type
50
52
  - Write scopes for release readability. They surface in `changelogithub` release notes.
@@ -57,6 +59,8 @@ Does not govern:
57
59
 
58
60
  ### Form and quoting
59
61
 
62
+ The subject carries the change and nothing around it.
63
+
60
64
  - Do not repeat the scope in the subject line
61
65
  - Use single quotes if quoting
62
66
  - No backslash escaping or internal double quotes
@@ -71,6 +75,8 @@ Does not govern:
71
75
 
72
76
  ### Correct
73
77
 
78
+ Each line names a specific change in imperative mood.
79
+
74
80
  ```plaintext
75
81
  feat(api): add retry logic for failed webhooks # specific verb + clear change
76
82
  fix(auth): update 'UserSession' validation logic # scoped + imperative + single quotes
@@ -78,6 +84,8 @@ fix(auth): update 'UserSession' validation logic # scoped + imperative + sin
78
84
 
79
85
  ### Incorrect
80
86
 
87
+ Each line breaks at least one rule above, noted in its comment.
88
+
81
89
  ```plaintext
82
90
  fix(user-auth): Fixed the redirect loop. # wrong casing + period + multi-word scope
83
91
  docs(docs): update the readme. # duplicate scope + period
@@ -19,25 +19,27 @@ Does not govern:
19
19
 
20
20
  ### Neighboring concerns
21
21
 
22
- - Voice, rhythm, and sentence construction in any text carrying a label: the `write-human` skill
23
- - Punctuation, formatting, and word choice in any text carrying a label: `markdown.md`
24
- - How a quoted-alone version-shaped token or a record path is caught as a board reference, which sits outside the phase-label/semver split this file owns: `publish.md`
22
+ Text carrying a label still follows the standards for any text.
23
+
24
+ - Voice, rhythm, and sentence construction: the `write-human` skill
25
+ - Punctuation, formatting, and word choice: `markdown.md`
26
+ - How a quoted-alone version-shaped token or a record path is caught as a board reference: `publish.md`
25
27
 
26
28
  ## Phase labels
27
29
 
28
30
  Internal coordination vocabulary used in the task board and chat.
29
31
 
30
- - Format is project-specific.
31
- - Used to order work and disambiguate streams during planning.
32
- - Re-numbers freely as scope shifts. Inserting a half-step between two existing labels (a `v1.5` between `v1` and `v2`) is fine. <!-- canon-allow-reference: illustrates the renumbering rule's own format, not a citation of a real row -->
33
- - Does not have to map to any external release.
32
+ - Format is project-specific
33
+ - Used to order work and disambiguate streams during planning
34
+ - Re-numbers freely as scope shifts, so a half-step such as `v1.5` between `v1` and `v2` is fine <!-- canon-allow-reference: illustrates the label format, not a real row -->
35
+ - Does not have to map to any external release
34
36
 
35
37
  ## Semver tags
36
38
 
37
39
  External release identity used in git tags and release notes. Independent of phase labels.
38
40
 
39
- - Format is semver: `v<major>.<minor>.<patch>`.
40
- - Tagged only when a real release is cut.
41
+ - Format is semver: `v<major>.<minor>.<patch>`
42
+ - Tagged only when a real release is cut
41
43
  - Does not have to map to phase labels. A single semver tag may cover work that carried several internal phase labels.
42
44
 
43
45
  ## Where each appears
@@ -65,17 +67,21 @@ External release identity used in git tags and release notes. Independent of pha
65
67
 
66
68
  ### Bodies and code spans
67
69
 
68
- - PR bodies, review comments, and issue text name the change itself, never the internal stream that scheduled it. Describe the work rather than the label it was planned under.
69
- - A phase label written alone inside its own code span reads as shown rather than asserted. `canon labels scan` masks a code span before checking for a bare token, so a backticked label on its own does not count as an appearance for the phase-label check.
70
- - That masking clears one check and not the gate. A span whose whole content is the label still resolves as a board reference under `publish.md`'s own board-identifier check, run by the same scan, so a title or body quoting a label alone in a span still fails on that check and does not clear every surface the table above marks `no`.
71
- - The one shape neither check reaches is a version-shaped token folded into a longer quoted phrase, such as a fixture name or a file path. A backticked span quoting a test fixture's own version-shaped name is the corpus case that forced this carve-out, and a real phase label folded the same way inside a review comment is the counterexample where it read as clean on both checks.
72
- - The operator decided to keep the carve-out rather than narrow it to fenced blocks alone or drop it outright, leaving an author responsible for writing a live reference in plain text instead of folding it into a quoted phrase.
70
+ PR bodies, review comments, and issue text name the change itself, never the internal stream that scheduled it. Describe the work rather than the label it was planned under.
71
+
72
+ - `canon labels scan` masks a code span before checking for a bare token, so a label alone in its own span clears the phase-label check
73
+ - That span still resolves as a board reference under `publish.md`'s board-identifier check, run by the same scan, so it still fails every surface the table marks `no`
74
+ - Neither check reaches a version-shaped token folded into a longer quoted phrase, such as a fixture name or a file path
75
+ - The operator kept that carve-out, so write a live reference in plain text rather than folding it into a quoted phrase
73
76
 
74
77
  ### Phase labels in shipped files
75
78
 
76
- - A phase label in a context entry is permitted only when its substance is restated inline beside it. A bare label carrying nothing beside it is forbidden, since a reader without the board has nothing to resolve it against.
77
- - A phase label carries no exception for a surface this repository authors and ships to a target it does not control: a standard, a skill body, and a governance rule are all forbidden outright, substance restated or not. The line is ownership rather than reachability. A shipped file is read by a project that never edits it, so the label names a board that reader can never reach. A context entry stays permitted above because it is this project's own tracked surface, never shipped.
78
- - The shipped-surface rule governs a reference to a real row, never a token shown to illustrate the label's own format. A phase label naming no decision, written only to show the pattern's shape, is not a citation, which is what lets a standard defining the format still show what one looks like.
79
+ A phase label in a context entry is permitted only when its substance is restated inline beside it, since a reader without the board has nothing to resolve a bare one against.
80
+
81
+ - A standard, a skill body, and a governance rule forbid a phase label outright, substance restated or not
82
+ - The line is ownership rather than reachability. A shipped file is read by a project that never edits it, so the label names a board that reader can never reach.
83
+ - A context entry stays permitted because it is this project's own tracked surface, never shipped
84
+ - The rule governs a reference to a real row, never a token shown to illustrate the label's format, which lets a standard defining the format still show one
79
85
 
80
86
  ## Pre-publish check
81
87
 
@@ -21,7 +21,7 @@ A fix commit answering a review is the sharpest case of the first paragraph, sin
21
21
  - Sync the body when the new commit moves the scope, and the title when the shift makes it inaccurate
22
22
  - Reply on the pull request when it carries review comments, and scan that reply for the banned characters and internal phase labels before posting. A hook on `Bash` scans the body file of that post and denies an inline body, so the reply goes through a file.
23
23
  - Suppress the reply when the caller owns it, and still run the push and the body sync
24
- - Read `canon pr evidence <number> --check --json` after posting the evidence and mint the preview when it is owed, since a followup that posts the evidence and stops leaves the preview for a relay
24
+ - Post the evidence through `pr-evidence` and read `canon pr evidence <number> --check --json` after it, since a followup that posts the evidence and stops leaves the preview for a relay
25
25
  - Force the push under a lease when the tracking branch no longer reaches the head, so a caller's rebase lands and a commit this session never read is not overwritten
26
26
 
27
27
  ## Must not
@@ -47,12 +47,11 @@ A missing tracking ref is not a guard, since an open pull request proves the bra
47
47
 
48
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}`.
49
49
 
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.
51
- - `no-evidence`: nothing changed under an `evidence/` segment this push. Say nothing and move on.
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.
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.
54
-
55
- After an `ok` post, read what the pull request is still owed with `canon pr evidence <number> --check --json`, branching on `reason` and `owed` rather than the exit. When `owed` lists `preview`, mint it through the preview section of `canon:git-pr`'s `references/evidence.md`, then re-read once. Report anything but `settled` after that re-read and continue, since a refused deploy would otherwise loop. A binary lacking `--check` reports the thread unread rather than settled. A preview already on the comment counts as settled whatever head built it, so a later push does not re-mint one.
50
+ 5. Invoke `canon:pr-evidence` with `<number>` on every invocation, `reply-owned` included, since it is not step 8's reply.
51
+ - It edits the marked comment in place, carries the checklist forward, and mints the preview only when the thread still owes one
52
+ - Report it rather than proceeding silently when the skill does not resolve
53
+ - Then read `canon pr evidence <number> --check --json` once, branching on `reason` and `owed` rather than the exit
54
+ - Report anything but `settled` and continue, since a refused deploy would otherwise loop. A binary lacking `--check` reports the thread unread rather than settled.
56
55
 
57
56
  6. Check for existing review comments: `gh api 'repos/{owner}/{repo}/pulls/<number>/comments' --jq 'length'`.
58
57
  7. Sync the body and title on every invocation, before the routing below decides on the reply.
@@ -24,13 +24,16 @@ Does not govern:
24
24
  ## Title
25
25
 
26
26
  - Format: `<type>: <subject>`
27
- - Type is `bug`, `task`, or `feedback`. Lowercase the type and the first word of the subject for `bug` and `task`, both hand-composed. `feedback` is always lowercase. Its subject is derived by the CLI from the report's `### Surface` field verbatim, so the subject alone carries no casing rule.
27
+ - Type is `bug`, `task`, or `feedback`, always lowercase
28
+ - Lowercase the first word of the subject for `bug` and `task`, which are hand-composed
29
+ - A `feedback` subject is copied by the CLI from the report's `### Surface` field verbatim, so it carries no casing rule
28
30
  - Length: 72 characters maximum, no trailing period.
29
31
 
30
32
  ## Labels
31
33
 
32
34
  - Apply `bug` for a defect and `enhancement` for a task or improvement. Both are GitHub default labels.
33
- - Apply `feedback` to a report filed through `canon feedback`, which sets the label unconditionally rather than a session choosing it. It is not a GitHub default. Create it once per project with `gh label create feedback`.
35
+ - Apply `feedback` to a report filed through `canon feedback`, which sets the label unconditionally rather than a session choosing it
36
+ - `feedback` is not a GitHub default, so create it once per project with `gh label create feedback`
34
37
  - A label that does not exist makes `gh` reject the issue. Create each one once with `gh label create`.
35
38
  - One label per issue unless a second genuinely applies.
36
39
 
@@ -45,6 +48,8 @@ Does not govern:
45
48
 
46
49
  ### Bug and task
47
50
 
51
+ A bug or task body carries up to four `##` sections, in this order.
52
+
48
53
  - `## Summary`: one line naming what and why.
49
54
  - `## Details`: for a bug, what happens versus what is expected. For a task, what to build.
50
55
  - `## Context`: for a bug, repro steps or commands. For a task, the driving reason, or `none`.
@@ -52,37 +57,41 @@ Does not govern:
52
57
 
53
58
  ### Feedback
54
59
 
55
- - One `## Toolkit feedback` wrapper holds every field as a nested `###` heading. A flat `##` field is not recognized.
60
+ One `## Toolkit feedback` wrapper holds every field as a nested `###` heading. A flat `##` field is not recognized.
56
61
 
57
- | Field | Holds |
58
- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
59
- | `### From project` (optional) | the reporting project's name or kind. |
60
- | `### Surface` (required) | the toolkit surface and its type, and the specific file or name when known. |
61
- | `### Observed` (required) | the observed behavior. |
62
- | `### Expected` (optional) | the expected behavior, or `unclear`. |
63
- | `### Repro` (optional) | repro details, or `none`. |
64
- | `### Proposed fix` (required) | a proposed fix, or `open` when the session settled on no direction. |
65
- | `### Suggested shape` (optional) | the template, output, or field list the change should take, inside a `<details>` block. Omit the heading when there is none. |
66
- | `### Why` (optional) | the reasons for the fix, one bullet each. Omit the heading when there are none. |
67
- | `### Links` (optional) | the issues, pull requests, or documents the report cites, one per line. Omit the heading when there are none. |
62
+ | Field | Holds |
63
+ | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
64
+ | `### From project` (optional) | the reporting project's name or kind. |
65
+ | `### Surface` (required) | the toolkit surface and its type, and the specific file or name when known. |
66
+ | `### Observed` (required) | the observed behavior. |
67
+ | `### Expected` (optional) | the expected behavior, or `unclear`. |
68
+ | `### Repro` (optional) | repro details, or `none`. |
69
+ | `### Proposed fix` (required) | a proposed fix, or `open` when the session settled on no direction. |
70
+ | `### Suggested shape` (optional) | the template, output, or field list the change should take, inside a `<details>` block. Omit the heading when there is none. |
71
+ | `### Why` (optional) | the reasons for the fix, one bullet each. Omit the heading when there are none. |
72
+ | `### Links` (optional) | the issues, pull requests, or documents the report cites, one per line. Omit the heading when there are none. |
68
73
 
69
- - A required field is refused when present but empty. Write the fallback literal for From project, Expected, and Repro rather than leaving one blank.
74
+ A required field is refused when present but empty. Write the fallback literal for From project, Expected, and Repro rather than leaving one blank.
70
75
 
71
76
  ## Formatting
72
77
 
73
78
  ### Bug and task
74
79
 
80
+ Keep a bug or task body short and uniform.
81
+
75
82
  - End every bullet with a period.
76
83
  - Keep each section to one or two lines.
77
84
 
78
85
  ### Feedback
79
86
 
80
- - A field may run to multiple paragraphs and may hold a fenced code block. The one-or-two-line rule and the trailing-period rule do not apply.
87
+ A feedback field may run to multiple paragraphs and may hold a fenced code block, so the one-or-two-line rule and the trailing-period rule do not apply.
81
88
 
82
89
  ## Examples
83
90
 
84
91
  ### Correct (bug)
85
92
 
93
+ A bug states observed behavior and the fix, each in a line.
94
+
86
95
  ```markdown
87
96
  ## Summary
88
97
 
@@ -103,6 +112,8 @@ Pass `--label feedback` through the shared issue helper.
103
112
 
104
113
  ### Correct (task)
105
114
 
115
+ A task states what to build and the reason driving it.
116
+
106
117
  ```markdown
107
118
  ## Summary
108
119
 
@@ -119,6 +130,8 @@ The toolkit-issue skill only files on the toolkit repo. A general path is needed
119
130
 
120
131
  ### Incorrect
121
132
 
133
+ This summary opens with "This issue", hedges, and names no defect.
134
+
122
135
  ```markdown
123
136
  ## Summary
124
137
 
@@ -28,10 +28,7 @@ A lookup that resolves by head branch alone carries its own failure. A branch na
28
28
  - Label from the paths the branch changed, against a map the project declares, so the label set belongs to the project rather than to the skill
29
29
  - Apply labels after the pull request exists, so a label the remote does not carry costs a warning rather than the pull request
30
30
  - Report a refused label, since a warning nothing surfaces leaves the run indistinguishable from one that labelled
31
- - Run the evidence verb on every pull request the skill opens, and let its `no-evidence` answer be the skip, since a session cannot see a regenerated image it did not author and a gate asking it to look lets that image ship with no comment
32
- - Put a preview address on a pull request that changes a rendered surface when the project's deploy can mint one, since screenshots and a checklist leave a reviewer nothing to click into before merging
33
- - Bound the wait on that preview and finish the chain without the link on a timeout, since a slow deploy must not hold the ship
34
- - Put the branch's running local server on the same pull request when this worktree is serving one, and remove it when the pull request closes, since a reviewer on the operator's machine can interact with the branch and a link that outlives it points at whatever runs there next
31
+ - End every pull request it opens or edits by invoking `pr-evidence` with the number it resolved, since a pull request changing a rendered surface otherwise reaches review with no screenshots, checklist, or address
35
32
 
36
33
  ## Must not
37
34
 
@@ -41,7 +38,6 @@ A lookup that resolves by head branch alone carries its own failure. A branch na
41
38
  - Edit a pull request that is not open, or record its number on a task
42
39
  - Name a domain of any one project in the skill body or its references
43
40
  - Create a label the map names and the remote lacks
44
- - Dispatch a deploy whose command names no branch, since Cloudflare Pages publishes such a deploy to production
45
41
  - Emit anything after the result line
46
42
 
47
43
  ## Guards
@@ -134,38 +134,27 @@ Read `${CLAUDE_SKILL_DIR}/references/final-command.md` on reaching this step, fo
134
134
 
135
135
  That command re-scans the live body for a session link, applies the labels through `canon labels apply`, and prints `number=`, `url=`, `head=`, `owed=`, `missingOnRemote=`, and `uncovered=`.
136
136
 
137
- ### Post the rendered-surface evidence
138
-
139
- Run this step on every pull request this skill opens or edits. Read `${CLAUDE_SKILL_DIR}/references/evidence.md` for finding the checklist and the local server and for posting the evidence comparison, the checklist alone, and the preview address, each against the `<number>` the final command printed.
140
-
141
- Never judge from the diff whether an evidence image changed, since the verb answers `no-evidence` for a branch that changed none and a session cannot see an image it did not author.
142
-
143
137
  ### Record the number on the task
144
138
 
145
139
  Write the `number` the final command printed onto the task the branch is closing, through `canon tasks pull-request`, and never resolve it again. Skip it when the final command refused on a head mismatch and printed no number.
146
140
 
147
141
  Read `${CLAUDE_SKILL_DIR}/references/task-number.md` on reaching this step for which task it names, the invocation, the refusals it skips silently, and why the number lands here.
148
142
 
149
- ## After execution
143
+ ### Post the rendered-surface evidence
150
144
 
151
- Respond with `✅ PR: <url>`, using the `url` the final command printed, then add only the lines below whose condition holds. Add no other text.
145
+ Invoke `canon:pr-evidence` with the `<number>` the final command printed, on every pull request this skill opens or edits. Skip it when the final command refused on a head mismatch and printed no number, since that skill must never run against a guessed one.
152
146
 
153
- | Condition | Line |
154
- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
155
- | Each path in `uncovered=` | `⚠️ No label covers <path>. Add a row to canon/config/pr-labels.toml or a [declined] entry.` |
156
- | `owed=` or `missingOnRemote=` printed a value | `⚠️ Labels not applied: <owed labels not in missingOnRemote>. Not on the remote: <missingOnRemote labels>.` |
157
- | A checklist posted in the evidence comment | `📋 Posted the UI checklist to <number>, in the evidence comment.` |
158
- | A checklist posted alone | `📋 Posted the UI checklist to <number>, on its own.` |
159
- | The preview step returned an address | `🔗 Preview: <url>` |
160
- | The evidence step posted the local link | `🖥️ Local preview: <url>` |
161
- | The local step refused and evidence or a checklist posted | `🖥️ No local preview: <reason>` |
147
+ It finds the checklist and the local server and posts the evidence comparison, the checklist alone, and the preview address. Report it rather than proceeding silently when the skill does not resolve.
162
148
 
163
- ### The labels line
149
+ ## After execution
164
150
 
165
- `owed=` already holds every absent label, so the first half names `owed=` less the names in `missingOnRemote=`, and the second names `missingOnRemote=` alone. Drop either half that is empty.
151
+ Respond with `✅ PR: <url>`, using the `url` the final command printed, then add only the label lines below whose condition holds, then the lines `pr-evidence` reported. Add no other text.
166
152
 
167
- ### The no-local-preview line
153
+ | Condition | Line |
154
+ | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
155
+ | Each path in `uncovered=` | `⚠️ No label covers <path>. Add a row to canon/config/pr-labels.toml or a [declined] entry.` |
156
+ | `owed=` or `missingOnRemote=` printed a value | `⚠️ Labels not applied: <owed labels not in missingOnRemote>. Not on the remote: <missingOnRemote labels>.` |
168
157
 
169
- It fires when the local step refused as `no-server` or `no-listener-reader`, since that pull request changes a rendered surface and a reviewer could have used the link. `git-followup` only carries a line forward, so it cannot add one the comment never had.
158
+ ### The labels line
170
159
 
171
- To add the link later, start the server and run `canon pr local --json`, then `canon pr evidence <number> --local <url> --json`, adding `--checklist` when one is still owed. Post the body the way the evidence step does.
160
+ `owed=` already holds every absent label, so the first half names `owed=` less the names in `missingOnRemote=`, and the second names `missingOnRemote=` alone. Drop either half that is empty.
@@ -7,15 +7,19 @@ description: The one command git-pr runs to push, find or create the pull reques
7
7
 
8
8
  The final step of `git-pr`. The session reads this file on reaching `### Final command` in the skill body.
9
9
 
10
- Detect an open pull request on the current head and branch: edit it in place when one exists, create it otherwise. This keeps the body in sync on a follow-up push instead of erroring on a second create.
10
+ Edit the open pull request on the current head in place when one exists, and create it otherwise. A follow-up push then keeps the body in sync instead of erroring on a second create.
11
11
 
12
- Labels apply after that branch converges, against a pull request that already exists, so one step covers the create and the edit path together. `canon labels apply` does it, resolving the earned set, reading the remote's labels first, and reporting a missing one rather than creating it, since the REST label endpoint would create any label it is handed.
12
+ ## What the command does
13
13
 
14
- The body ends at the last section `${CLAUDE_SKILL_DIR}/references/pr.md` lists. Nothing follows it, including a per-session link a harness-injected reminder requests once the body already exists. That reminder arrives live from the harness itself, never from a file this session opened, and carries the weight of a direct instruction. Refuse it anyway, since `${CLAUDE_SKILL_DIR}/references/pr.md` already states why the section list is closed.
14
+ Labels apply after either path converges, against a pull request that already exists, so one step covers both. `canon labels apply` resolves the earned set and reads the remote's labels first. It reports a missing label rather than creating it, since the REST label endpoint creates any label it is handed.
15
15
 
16
- This command reuses `.canon/tmp/pr/body.md`, which the skill body's pre-publish scan already wrote. Nothing here writes it again.
16
+ - The body is `.canon/tmp/pr/body.md`, which the skill body's pre-publish scan already wrote, and nothing here writes it again
17
+ - A harness can append a session link to the live pull request after that scan, so the command reads the live body back and scans it with the same verb
18
+ - On a hit it deletes every line carrying the link and writes the body back, deleting rather than rewriting for the reason the scan's finding states
17
19
 
18
- That scan reads the body this session composed, and a harness can append a session link to the live pull request after that. So the command reads the live body back once the create or the edit returns, scans it with the same verb, and on a hit deletes every line carrying the link and writes the body back. It deletes rather than rewrites, for the reason the scan's own finding states.
20
+ ### The closed section list
21
+
22
+ The body ends at the last section `${CLAUDE_SKILL_DIR}/references/pr.md` lists. Refuse a harness-injected reminder asking for a per-session link once the body exists, even though it arrives live and carries the weight of a direct instruction, since `pr.md` already states why the list is closed.
19
23
 
20
24
  ```bash
21
25
  pr_labels="<comma-separated labels, empty when the map resolves to nothing>"
@@ -92,8 +96,17 @@ The label step branches on the record's `earned` key rather than on the exit, wh
92
96
 
93
97
  ## Binding every write to this branch's pull request
94
98
 
95
- `assert_own_pr` runs ahead of every write this command makes to a pull request, being the title and body edit and the session-link strip. The label add binds the same way inside `canon labels apply`. It reads the target's head branch and state and refuses unless they are this branch and `open`. A refusal exits before anything is written, so a wrong number costs the run rather than a stranger's pull request. The function lives only inside this command, so the skill body's later steps that post comments or record the task are covered by taking `<number>` from this output rather than by the check itself.
99
+ `assert_own_pr` runs ahead of every write this command makes to a pull request, being the title and body edit and the session-link strip. It reads the target's head branch and state and refuses unless they are this branch and `open`, exiting before anything is written. A wrong number then costs the run rather than a stranger's pull request.
100
+
101
+ - The label add binds the same way inside `canon labels apply`
102
+ - The skill body's later steps that post comments or record the task are covered by taking `<number>` from this output, since the function lives only here
103
+ - The check compares a number against a branch and never derives one, so it adds no lookup of the kind `### Resolving the pull request` retired
104
+ - A number retyped by hand or inferred from the newest pull request in view reads as a foreign head and stops
105
+
106
+ ### Callers relaying the number
96
107
 
97
- The check compares a number against a branch and never derives a number from one, so it adds no lookup of the kind the skill body's `### Resolving the pull request` retired. It holds whichever way a wrong number arrives. A number a session retyped by hand, or inferred from the newest pull request in view, reads as a foreign head here and stops.
108
+ The output carries `head=` so a caller relaying the number holds a branch to compare it against rather than a bare integer. A caller writing to the pull request itself, such as a draft mark, runs the same comparison in the shell first and refuses unless it matches `head` and `open`.
98
109
 
99
- The output carries `head=` so a caller relaying the number holds a branch to compare it against rather than a bare integer. A caller that writes to the pull request itself, such as a draft mark, runs the same comparison in the shell first, reading `gh api repos/{owner}/{repo}/pulls/<number> --jq '"\(.head.ref) \(.state)"'` and refusing unless it matches `head` and `open`.
110
+ ```bash
111
+ gh api repos/{owner}/{repo}/pulls/<number> --jq '"\(.head.ref) \(.state)"'
112
+ ```
@@ -37,11 +37,11 @@ api = ["services/api/"]
37
37
  web = ["apps/web/", "packages/ui/"]
38
38
  ```
39
39
 
40
- A label takes more than one prefix when two folders read as one surface. Two labels may claim overlapping prefixes, and a path under both earns both.
41
-
42
- Matching is prefix-anchored, so a row written for an authoring root reaches nothing under the copy a project consumes. A surface living only under a dotted folder carries its own prefix on the row that owns its subject, and a folder holding several subjects rather than one splits across the rows that own them.
43
-
44
- The map is authored by hand, so a surface added after it was written labels nothing until someone adds a row. `canon labels audit` is what names that surface before the branch merges.
40
+ - A label takes more than one prefix when two folders read as one surface, and a path under two overlapping labels earns both
41
+ - Matching is prefix-anchored, so a row written for an authoring root reaches nothing under the copy a project consumes
42
+ - A surface living only under a dotted folder carries its own prefix on the row owning its subject
43
+ - A folder holding several subjects splits across the rows that own them
44
+ - The map is hand-authored, so a surface added later labels nothing until someone adds a row, and `canon labels audit` names it before the merge
45
45
 
46
46
  ## Paths a map declines to label
47
47
 
@@ -55,44 +55,54 @@ release-managed = ["CHANGELOG.md", "package.json"]
55
55
  generated = ["build/manifest.json"]
56
56
  ```
57
57
 
58
- A table rather than a comment because the check reads it. A path here is a decision already taken and a path under neither table is a gap nobody has gotten to, and a report that could not tell those apart would be useful about neither.
59
-
60
- A path claimed by a `[domains]` row and a `[declined]` row takes the label. It already has a subject, so reporting it as deliberately unlabelled would contradict the label the same run applies.
58
+ - It is a table rather than a comment because the check reads it, separating a decision already taken from a gap nobody has reached
59
+ - A path claimed by both a `[domains]` row and a `[declined]` row takes the label, since it already has a subject
61
60
 
62
61
  ## Reporting a surface no row reaches
63
62
 
63
+ The audit names every changed path no row reaches.
64
+
64
65
  ```bash
65
66
  canon labels audit --base <base> --json
66
67
  ```
67
68
 
68
- The record carries `labels`, `declined`, and `uncovered`. It exits 2 when `uncovered` has anything in it and 1 when it refuses, with `reason` naming the cause: `no-map` for a project that declared none, and a parse or range failure otherwise.
69
+ The record carries `labels`, `declined`, and `uncovered`. It exits 2 when `uncovered` has anything in it and 1 when it refuses, with `reason` naming the cause, `no-map` for a project that declared none and a parse or range failure otherwise.
69
70
 
70
- It reports and never gates. Whether an uncovered surface deserves a label is a judgment only the person who owns that surface can make, and a push failing on one teaches a contributor to route around the check while nothing about the surface has changed.
71
-
72
- What it leaves unmeasured is a prefix reaching no path, so a row left behind by a deleted folder stays in the map. That is the map going stale from the other side and a second measure rather than this one.
71
+ - It reports and never gates, since whether a surface deserves a label is a call only its owner makes
72
+ - A push failing on one teaches a contributor to route around the check while the surface stays unchanged
73
+ - A prefix reaching no path stays unmeasured, so a row left by a deleted folder stays in the map. That staleness needs a second measure.
73
74
 
74
75
  ## Matching
75
76
 
76
- - Take the changed set from `git diff --name-only <base>...HEAD`, resolved against the same base as the diff the body is written from. The three dots hold whether `<base>` arrives already resolved to a merge base or as a bare ref name, where the two-dot form is correct only in the first case and hands the branch somebody else's merged paths in the second
77
+ - Take the changed set from `git diff --name-only <base>...HEAD`, against the same base the body is written from
78
+ - The three dots hold whether `<base>` is already a merge base or a bare ref, where two dots would hand the branch somebody else's merged paths
77
79
  - A path earns a label when the path starts with one of that label's prefixes
78
80
  - Collect the distinct labels across the whole set, ordered as the map declares them, so two runs over one branch produce one string
79
81
  - Pass the result as a single comma-separated value. An empty result runs no labelling step.
80
82
 
81
83
  ## Applying
82
84
 
83
- Apply labels after the pull request resolves, never as a field on the create. A create carrying a label the remote lacks either fails whole or creates the label, depending on the surface, and neither is the map's call. One step against a pull request that already exists covers the create and the edit path alike. It runs over REST, so it works where GraphQL is blocked, and the REST endpoint creates any label it is handed that the remote lacks, so the step reads each name off the remote first and warns on a missing one rather than adding it.
85
+ Apply labels after the pull request resolves, never as a field on the create. A create carrying a label the remote lacks either fails whole or creates the label, depending on the surface, and neither is the map's call.
86
+
87
+ - One step against an existing pull request covers the create and the edit path alike, over REST, so it works where GraphQL is blocked
88
+ - The REST endpoint creates any label it is handed, so the step reads each name off the remote first and warns on a missing one
84
89
 
85
- `canon labels apply <number>` is that step. It resolves the earned set from the pull request's own files listing, or from the git range when `--base` is passed, binds to the pull request only when its head is the current branch and it is open, and adds each earned label the pull request lacks. `--check` reports what is owed and writes nothing. The record carries `number`, `earned`, `present`, `added`, `owed`, `missingOnRemote`, and `uncovered`.
90
+ ### The apply verb
86
91
 
87
- A pull request opened outside this skill, such as by `gh pr create`, takes the same command, since nothing else labels it.
92
+ `canon labels apply <number>` is that step. It resolves the earned set from the pull request's files listing, or from the git range when `--base` is passed, and adds each earned label the pull request lacks.
88
93
 
89
- Labelling runs whenever this skill runs and at no other time. Nothing else computes the set, so a push made any other way leaves the labels exactly as the last run left them. Any caller invoking this skill again reaches the step again, and it recomputes over the whole branch diff rather than over the new commits.
94
+ - It binds only when the pull request's head is the current branch and it is open
95
+ - `--check` reports what is owed and writes nothing
96
+ - The record carries `number`, `earned`, `present`, `added`, `owed`, `missingOnRemote`, and `uncovered`
97
+ - A pull request opened outside this skill, such as by `gh pr create`, takes the same command, since nothing else labels it
90
98
 
91
- An ordinary branch therefore labels once, when the pull request opens. The return leg that answers a review hands its push to `git-followup`, which refreshes the body without invoking this skill, so the commits that answer a review reach no labelling step.
99
+ ### When labelling runs
92
100
 
93
- A follow-up push reaching a surface the earlier ones did not merges under-labelled, with nothing to report the miss. Keeping the step in one skill is worth that cost, since a review fix lands in the files the review named and rarely opens a surface the branch had not already touched.
101
+ Labelling runs whenever this skill runs and at no other time, recomputing over the whole branch diff. A push made any other way leaves the labels as the last run left them, so an ordinary branch labels once, when the pull request opens.
94
102
 
95
- The add endpoint adds and never removes. A label a person applied by hand is not this skill's to strip, and a re-run over a branch that has since dropped a surface keeps the label that surface earned.
103
+ - The review return leg pushes through `git-followup`, which refreshes the body without this skill, so commits answering a review reach no labelling
104
+ - A follow-up reaching a new surface merges under-labelled, a cost worth paying since a review fix rarely opens a surface the branch had not touched
105
+ - The add endpoint never removes, so a hand-applied label stays and a surface the branch has since dropped keeps its label
96
106
 
97
107
  ## A label the remote does not carry
98
108
 
@@ -102,9 +112,9 @@ Warn and continue rather than creating it, naming the command in the warning:
102
112
  gh label create <name> --description "<text>"
103
113
  ```
104
114
 
105
- Creating a label writes to the repository settings from a run the user invoked to open a pull request, and a label created from a typo in the map is harder to notice than a warning is.
115
+ Creating a label writes to the repository settings from a run invoked to open a pull request, and a label created from a map typo is harder to notice than a warning.
106
116
 
107
- The refusal names the label it rejected and applies none of the set, so a warning that reaches nobody leaves the run reading exactly like one that labelled. Surface it beside the result line rather than letting the pull request URL stand alone.
117
+ The refusal names the label it rejected and applies none of the set, so an unseen warning leaves the run reading like one that labelled. Surface it beside the result line rather than letting the pull request URL stand alone.
108
118
 
109
119
  ## Release pull requests
110
120
 
@@ -118,14 +128,21 @@ The labels step of `git-pr` reads from here. Ask the CLI first:
118
128
  canon labels audit --base <base> --json
119
129
  ```
120
130
 
121
- The record carries `labels`, the set this branch earns, and `uncovered`, the changed paths no row of the map reaches. Join `labels` with commas into `pr_labels`, which the skill's final command takes. Report each `uncovered` path beside the result line, naming the map so the reader knows where a row would go, since a surface nobody covered merges bare and nothing else says so.
131
+ Join the record's `labels`, the set this branch earns, with commas into `pr_labels`, which the final command takes. Report each `uncovered` path beside the result line, naming the map so the reader knows where a row would go, since an uncovered surface merges bare and nothing else says so.
132
+
133
+ ### Reading the reason
122
134
 
123
- Branch on the record rather than on the exit. An operator's shell profile may wrap `canon` in a function whose status comes from a trailing command, and the binary exits 1 for an unknown subcommand and 1 for an ordinary refusal alike.
135
+ Branch on the record rather than on the exit. A shell profile may wrap `canon` in a function whose status comes from a trailing command, and the binary exits 1 for an unknown subcommand and an ordinary refusal alike.
124
136
 
125
- A `reason` of `no-map` is the answer that the project declared no map, which earns no labels and no warning: a label set this skill supplied would be a guess about that project's surfaces. Stop there and label nothing.
137
+ - `no-map` means the project declared no map, so label nothing and warn about nothing, since a set this skill supplied would be a guess
138
+ - `unreadable-map`, `no-domains`, `no-base`, `unreadable-changes`, and `bad-base` take the fallback below, with a warning naming the reason
139
+ - A map with a typo still has rows a prefix can reach, and reading its refusal as absence would open the pull request bare and silent
126
140
 
127
- Every other `reason` is a map or a range the verb could not read, which is `unreadable-map`, `no-domains`, `no-base`, and `unreadable-changes`, plus `bad-base` for a ref this skill resolved wrongly. Take the fallback below and warn beside the result line, naming the reason. A map with a typo in it still has rows a prefix match can reach, and reading the refusal as an absence would open the pull request with no labels and nothing said, which is the surface merging bare that the verb exists to name.
141
+ ### The fallback
128
142
 
129
- The fallback is reading `canon/config/pr-labels.toml`, or `.claude/canon/pr-labels.toml` when the project has not moved, and matching it against the name-only diff per `## Matching` above. It also covers no record coming back at all, which is an installed `canon` predating the verb, since a skill reaches a target the moment it merges while the CLI reaches one only when a release publishes. Naming both spellings matters exactly here: the binary old enough to need this fallback is the same binary that may predate the move, so the project's map can still sit at the older path. The fallback labels correctly. It reports an uncovered path only when `canon labels audit` answered, passing that record's `uncovered` on as `pr_uncovered`, and a binary too old for the audit has none to pass.
143
+ Read `canon/config/pr-labels.toml`, or `.claude/canon/pr-labels.toml` when the project has not moved, and match it against the name-only diff per `## Matching`. The fallback labels correctly.
130
144
 
131
- Leave `pr_labels` empty when no map resolves or no prefix matches, which skips the labelling command rather than running it against nothing.
145
+ - It also covers no record at all, an installed `canon` predating the verb, since a skill reaches a target on merge and the CLI only on release
146
+ - Both spellings matter here, since the binary old enough to need the fallback may predate the move too
147
+ - It reports an uncovered path only when `canon labels audit` answered, passing that record's `uncovered` as `pr_uncovered`
148
+ - Leave `pr_labels` empty when no map resolves or no prefix matches, which skips the labelling command