@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
package/README.md CHANGED
@@ -12,7 +12,6 @@
12
12
  [![License MIT](https://img.shields.io/npm/l/@erclx/canon)](LICENSE)
13
13
 
14
14
  canon is a CLI and Claude Code plugin that runs your agent's work from plan to merge, holds it to the practices that make code worth keeping, and installs the same setup in every repository.
15
-
16
15
  **[See it run at canon.erclx.dev](https://canon.erclx.dev)**
17
16
 
18
17
  </div>
@@ -21,9 +20,13 @@ That page is one real session the toolkit ran on itself, and every count on it i
21
20
 
22
21
  ## Why
23
22
 
24
- If an agent writes code for you in more than one repository, this is for you. canon runs that work from plan to merge. A planner writes the plan, a worker builds it on its own branch, and a separate session reviews the pull request. It holds the work to a practice, with rules that load when a matching path is edited and checks that fail when a rule is ignored. Both run on one setup, installed the same way in every repository you keep.
23
+ If an agent writes code for you in more than one repository, this is for you. canon runs that work from plan to merge. A planner writes the plan, a worker builds it on its own branch, and a separate session reviews the pull request.
24
+
25
+ It holds the work to a practice, with rules that load when a matching path is edited and checks that fail when a rule is ignored. Both run on one setup, installed the same way in every repository you keep.
25
26
 
26
- That shared setup is what the rest of this page describes, and it is the part that drifts without a source. Every AI coding setup accumulates the same assets. Prompts to reuse, rules agents should follow, slash commands, skills, seed docs, sync scripts. Once you have enough projects, your copies drift and your agents stop getting consistent signals.
27
+ ### The shared setup
28
+
29
+ That shared setup is what the rest of this page describes, and it drifts without a source. Every AI coding setup accumulates the same assets. Prompts to reuse, rules agents should follow, slash commands, skills, seed docs, sync scripts. Once you have enough projects, your copies drift and your agents stop getting consistent signals.
27
30
 
28
31
  Three design choices shape the toolkit.
29
32
 
@@ -61,17 +64,21 @@ bun install --global @erclx/canon
61
64
 
62
65
  Each domain has a canonical source in this repo and a thin install or sync CLI on your side. The domains split on one line: some are copied into your project and become yours to edit, and some are never copied at all.
63
66
 
64
- | Domain | What it is | How it reaches you |
65
- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
66
- | Plugin skills | Skills that plan a feature, review a diff, sync the planning docs, and run the ship chain | Loaded live from the plugin, never copied |
67
- | Governance | Coding and authoring rules that load into a Claude session when a matching path is edited | Installed per project by `canon gov install`, refreshed by sync |
68
- | Tooling stacks | Golden configs, seeds, and a reference per framework | Laid down by `canon init`, reconciled by `canon tooling sync` |
69
- | Design system | Ships a `DESIGN.md` intent format that points at the project's stylesheet for values, a skill that drafts one from an existing project or from scratch, and a render command | `canon design render` |
70
- | Slides | A folder of HTML slides per deck, drawn in the project's tokens and rebuilt as editable shapes | `canon slides render` writes PowerPoint |
71
- | Teach | Lessons, sources, and a glossary in one workspace, rendered to a small site | `canon teach` |
72
- | Canvas | Pages of HTML frames on a local pan and zoom surface, each frame drawn with the project's tokens | `canon canvas serve` |
73
- | Transcripts | A YouTube transcript with metadata frontmatter | `canon transcripts <url>` writes it into any repo |
74
- | Sandbox | Scenario scaffolds that provision an isolated project state for verifying each domain flow | `canon sandbox` |
67
+ ### Domain catalog
68
+
69
+ | Domain | What it is | How it reaches you |
70
+ | -------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
71
+ | Plugin skills | Skills that plan a feature, review a diff, sync the planning docs, and run the ship chain | Loaded live from the plugin, never copied |
72
+ | Governance | Coding and authoring rules that load into a Claude session when a matching path is edited | Installed per project by `canon gov install`, refreshed by sync |
73
+ | Tooling stacks | Golden configs, seeds, and a reference per framework | Laid down by `canon init`, reconciled by `canon tooling sync` |
74
+ | Design system | Ships a `DESIGN.md` intent format pointing at your stylesheet for values, a skill that drafts one, and a render command | `canon design render` |
75
+ | Slides | A folder of HTML slides per deck, drawn in the project's tokens and rebuilt as editable shapes | `canon slides render` writes PowerPoint |
76
+ | Teach | Lessons, sources, and a glossary in one workspace, rendered to a small site | `canon teach` |
77
+ | Canvas | Pages of HTML frames on a local pan and zoom surface, each frame drawn with the project's tokens | `canon canvas serve` |
78
+ | Transcripts | A YouTube transcript with metadata frontmatter | `canon transcripts <url>` writes it into any repo |
79
+ | Sandbox | Scenario scaffolds that provision an isolated project state for verifying each domain flow | `canon sandbox` |
80
+
81
+ ### How domains reach you
75
82
 
76
83
  Those domains reach you in three shapes. A tooling stack lands as real files under version control, because a config is something your build reads and your project owns. A plugin skill and the references it carries are never copied, so there is no copy in your repo to drift from this one.
77
84
 
@@ -117,6 +124,8 @@ The first two update the skills, the third updates the CLI, and they move indepe
117
124
 
118
125
  `canon upgrade` reads the package manager off its own install path and reinstalls with that one, so you don't have to remember which put it there. It names what it detected before it runs anything, and it refuses a source checkout rather than reinstalling over your clone.
119
126
 
127
+ ### Checking for drift
128
+
120
129
  You don't have to wait until something breaks to find out you're behind. `canon sync --check` and `canon claude skills drift` both report the installed version against the newest published one, and neither changes its exit code over it, so an offline machine reads unknown rather than red.
121
130
 
122
131
  To stop doing this by hand, turn auto-update on once under `/plugin` in the Marketplaces tab. Confirm what you are running with `canon --version` and `claude plugin list`.
@@ -127,6 +136,8 @@ Working on the toolkit starts from a clone. Running the CLI doesn't, since it in
127
136
 
128
137
  ### Prerequisites
129
138
 
139
+ Bun, Git, and a shell are required, and the GitHub CLI is optional.
140
+
130
141
  - [Bun](https://bun.sh) for the CLI runtime and scripts
131
142
  - [Git](https://git-scm.com) with worktree support
132
143
  - [GitHub CLI](https://cli.github.com) (optional) for ship flows
@@ -141,7 +152,10 @@ bun install
141
152
  bun run bootstrap
142
153
  ```
143
154
 
144
- The script is idempotent, so re-run it after pulling upstream changes without duplicating anything. That also means it leaves an alias block you already have alone rather than refreshing it, so an alias added upstream needs the block deleted and the script re-run. It confirms the install by resolving `canon --help` on the last step. See [zshrc aliases](docs/workflow/zshrc-aliases.md) for what each alias does, how to pick up a new one, and how to opt out of the block.
155
+ The script is idempotent, so re-run it after pulling upstream changes without duplicating anything. It confirms the install by resolving `canon --help` on the last step.
156
+
157
+ - It leaves an alias block you already have alone rather than refreshing it, so an alias added upstream needs the block deleted and the script re-run.
158
+ - [Zshrc aliases](docs/workflow/zshrc-aliases.md) covers what each alias does, how to pick up a new one, and how to opt out of the block.
145
159
 
146
160
  With the CLI linked, scaffold a fresh project.
147
161
 
@@ -151,9 +165,12 @@ git init
151
165
  canon init
152
166
  ```
153
167
 
154
- `canon init` installs base tooling configs, Claude seeds, and governance rules in one pass, and scaffolds a `.claude/wiki/` stub for your project's own reference pages. Governance defaults to the `base` stack, so a bare init lands the workflow and doc-authoring rules in `.claude/rules/`. Each rule names the skill it loads, so no corpus is copied into your project.
168
+ `canon init` installs base tooling configs, Claude seeds, and governance rules in one pass, and scaffolds a `.claude/wiki/` stub for your project's own reference pages.
155
169
 
156
- Pass `--stack <name>` for a language stack such as `node` or `python`, or `--skip governance` to leave rules out. Run `canon tooling list --json` to see the catalog.
170
+ - Governance defaults to the `base` stack, so a bare init lands the workflow and doc-authoring rules in `.claude/rules/`.
171
+ - Each rule names the skill it loads, so no corpus is copied into your project.
172
+ - Pass `--stack <name>` for a language stack such as `node` or `python`, or `--skip governance` to leave rules out.
173
+ - Run `canon tooling list --json` to see the catalog.
157
174
 
158
175
  For the full journey from scaffold through adding a domain later to syncing upstream drift, see [target projects](docs/target/projects.md).
159
176
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
4
- "version": "5.40.0",
4
+ "version": "5.42.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -5,36 +5,56 @@ description: Which external API design sources this skill adopted, which it decl
5
5
 
6
6
  # Adopted and declined
7
7
 
8
- External sources were read and filtered rather than imported. This file is the record. A later session extending the skill adds to it rather than re-arguing an item already settled here.
8
+ External sources were read and filtered rather than imported. This file is the record, and a later session extending the skill adds to it rather than re-arguing an item settled here.
9
9
 
10
- ## Adopted
10
+ ## Adopted from the default shelf
11
11
 
12
- ### From the default shelf
12
+ ### Observable behavior
13
13
 
14
- **Hyrum's Law.** [Software Engineering at Google, ch. 1](https://abseil.io/resources/swe-book/html/ch01.html): with enough users, every observable behavior of a system is depended on by somebody, whatever the contract promises. Adopted as the opening group, stated as design rules rather than as an observation, so a session decides what is observable before a caller decides it for them. Ordering and timing are named because the chapter's own examples are hash iteration order and timing.
14
+ [Software Engineering at Google, ch. 1](https://abseil.io/resources/swe-book/html/ch01.html) states Hyrum's Law, that with enough users, every observable behavior of a system is depended on by somebody, whatever the contract promises.
15
15
 
16
- **The One-Version Rule.** [Software Engineering at Google, ch. 21](https://abseil.io/resources/swe-book/html/ch21.html): a repository keeps one version of each dependency, so a change to it migrates every caller rather than leaving two versions live. Adopted under additive change for a caller that cannot pin, which is any module imported from inside one repository.
16
+ Adopted as the opening group, stated as design rules rather than as an observation, so a session decides what is observable before a caller decides it for them. Ordering and timing are named because the chapter's own examples are hash iteration order and timing.
17
17
 
18
- ### From one external skill
18
+ ### One version per dependency
19
19
 
20
- From `addyosmani/agent-skills@2686b620`, [`skills/api-and-interface-design/SKILL.md`](https://github.com/addyosmani/agent-skills/blob/2686b620fc1fed2e8f60c704839c766b8594c6b6/skills/api-and-interface-design/SKILL.md):
20
+ [Software Engineering at Google, ch. 21](https://abseil.io/resources/swe-book/html/ch21.html) states the One-Version Rule, keeping one version of each dependency in a repository, so a change to it migrates every caller rather than leaving two versions live. Adopted under additive change for a caller that cannot pin, which is any module imported from inside one repository.
21
+
22
+ ## Adopted from one external skill
23
+
24
+ From `addyosmani/agent-skills@2686b620`, [`skills/api-and-interface-design/SKILL.md`](https://github.com/addyosmani/agent-skills/blob/2686b620fc1fed2e8f60c704839c766b8594c6b6/skills/api-and-interface-design/SKILL.md).
25
+
26
+ ### Contract and errors
21
27
 
22
28
  **Contract first.** Write the interface before the body. Adopted as its own group, widened from an endpoint to a signature, a flag set, an output shape, and a schema.
23
29
 
24
- **One error shape.** Every failure on one surface carries the same structure. Adopted with a stable code added for the caller to branch on, and a clause keeping internal implementation details out of the message, since no project rule carries either for every code stack.
30
+ **One error shape.** Every failure on one surface carries the same structure. Adopted with a stable code for the caller to branch on, and internal details kept out of the message, since no project rule carries either for every code stack.
31
+
32
+ ### Input and change
25
33
 
26
34
  **Validation at the boundary.** Check input where it enters and trust it inside. Adopted, with a value read back from a file or another service counted as input.
27
35
 
28
- **Additive change.** New fields optional, nothing removed in place. Adopted, with a change of meaning under an existing name added as its own ban, since it breaks a caller as surely as a removal and no rename shows in the diff.
36
+ **Additive change.** New fields optional, nothing removed in place. Adopted, with a change of meaning under an existing name as its own ban, since it breaks a caller as surely as a removal and no rename shows in the diff.
37
+
38
+ ### Idempotency
29
39
 
30
- **Idempotency.** A stored key honored on retry, an atomic claim under a unique constraint, a reused key with a different body failing loudly, and retention past the longest retry. Adopted as four rules, keeping the reused-key rule, which is the one most often left out.
40
+ A stored key honored on retry, an atomic claim under a unique constraint, a reused key with a different body failing loudly, and retention past the longest retry. Adopted as four rules, keeping the reused-key rule, which is the one most often left out.
31
41
 
32
- **Excuses, red flags, and a closing checklist.** The same source's skill anatomy, adopted toolkit-wide through `${CLAUDE_SKILL_DIR}/../create-skill/references/skill-practice.md`. Each excuse row here rebuts a reason a session gives when it is about to break a caller, such as "nobody depends on that field".
42
+ ### The closing sections
43
+
44
+ The same source's skill anatomy supplies the excuses, red flags, and closing checklist, adopted toolkit-wide through `${CLAUDE_SKILL_DIR}/../create-skill/references/skill-practice.md`. Each excuse row here rebuts a reason a session gives when about to break a caller, such as "nobody depends on that field".
33
45
 
34
46
  ## Declined
35
47
 
36
- **Protocol-specific guidance.** The source skill carries verbs, status codes, pagination, and URL shapes for one protocol style. Declined because the body has to hold for a function's exports, a command's flags, and a file format as much as an endpoint, and a protocol section would route every non-network surface to rules that do not apply. A project needing those conventions states them in its own stack rule.
48
+ Each item below was read and left out, with the reason it changes nothing a session does here.
49
+
50
+ ### Protocol-specific guidance
51
+
52
+ The source skill carries verbs, status codes, pagination, and URL shapes for one protocol style. Declined because the body holds for a function's exports, a command's flags, and a file format as much as an endpoint, so a protocol section would route every non-network surface to rules that do not apply.
53
+
54
+ A project needing those conventions states them in its own stack rule.
55
+
56
+ ### Depth and versions
37
57
 
38
- **Interface depth.** Ousterhout's deep modules, after [A Philosophy of Software Design](https://web.stanford.edu/~ouster/cgi-bin/book.php), judge how much a module hides behind its surface. Declined here because depth is about what sits behind a boundary, and this skill owns only what crosses it. `code-craft` carries it.
58
+ **Interface depth.** Ousterhout's deep modules, after [A Philosophy of Software Design](https://web.stanford.edu/~ouster/cgi-bin/book.php), judge how much a module hides behind its surface. Depth is about what sits behind a boundary, and this skill owns only what crosses it, so `code-craft` carries it.
39
59
 
40
- **Versioned surfaces as the default.** Shipping a new version beside the old for every breaking change. Declined as the default because additive change avoids the break in the first place, and two live versions split the callers. The One-Version Rule covers a caller that cannot pin.
60
+ **Versioned surfaces as the default.** Shipping a new version beside the old for every breaking change. Additive change avoids the break in the first place and two live versions split the callers, so the One-Version Rule covers a caller that cannot pin.
@@ -19,6 +19,8 @@ Without this skill, the run from an approved plan to an open pull request is a c
19
19
  - Open the pull request as a draft before the continuous integration watch begins, since a pull request marked after it is mergeable for the length of the run
20
20
  - Continue past a produced UI checklist rather than stopping on it, and report its unchecked boxes in the closing block, since the draft mark already holds the merge
21
21
  - Prove the draft target's head is the current branch and its state is open before marking it, and stop with nothing marked on a mismatch, since the number is retyped between steps and a draft mark on a release pull request holds that release
22
+ - Stop a fan-in member after its verify and test order with a push and a done message rather than a pull request, and open an integrator's build by merging the member branches, since parallel batches sharing edge files put every open sibling into conflict on each merge
23
+ - Wait for both review families through `canon pr review-state --wait` once CI passes on a launch that named a controller, which alone dispatches the reviewers, bounded at the lift's own default, and hand the record to the controller on a timeout, since a worker improvising the read polled issue comments, never saw a UI close posted as a review, and stalled with both reviewers idle
22
24
  - Name the recovery for the stop it took, since the value of stopping is that the user knows where to resume
23
25
 
24
26
  ## Must not
@@ -73,6 +73,8 @@ When this run decides against a suggestion, rewrite its line as `overridden at e
73
73
 
74
74
  Load `canon:build-in-slices`, reporting it when it does not resolve, and implement only what the plan describes. Do not expand scope, refactor neighbors, or touch files outside the plan's "Files to touch" list without reason.
75
75
 
76
+ When the plan's `**Constraints:**` carry a `Fans in` bullet, this run is a fan-in integrator, so open this step by merging the member branches, per `${CLAUDE_SKILL_DIR}/references/fan-in.md`.
77
+
76
78
  ## Step 3: verify
77
79
 
78
80
  Run the verify commands defined in `CLAUDE.md` (lint, typecheck, tests). On failure:
@@ -97,11 +99,15 @@ The verb reads git history rather than the working tree, so it catches a violati
97
99
 
98
100
  Never read a missing subcommand as clean. Read `${CLAUDE_SKILL_DIR}/references/verb-absent.md` on meeting one.
99
101
 
102
+ ### A fan-in member stops here
103
+
104
+ When the plan's `**Constraints:**` carry a `Fan-in member of` bullet, push the branch, announce it done, and stop before Step 5. A member opens no pull request, since its integrator ships the whole set. Read `${CLAUDE_SKILL_DIR}/references/fan-in.md` for the push and the message.
105
+
100
106
  ## Step 5: UI checklist (conditional)
101
107
 
102
108
  If the diff touches UI files (JSX, TSX, Vue, Svelte, HTML, or CSS under `src/`), invoke `canon:ui-checklist`, then continue to Step 6 whatever it returns, in every run. The Step 6 draft mark holds the merge, so a stop here protects nothing.
103
109
 
104
- When it produces a checklist, read `${CLAUDE_SKILL_DIR}/references/ui-checklist.md` for the counts to hold before `git-pr` removes the file.
110
+ When it produces a checklist, read `${CLAUDE_SKILL_DIR}/references/ui-checklist.md` for the counts to hold before `pr-evidence` removes the file.
105
111
 
106
112
  ## Step 6: ship
107
113
 
@@ -113,9 +119,29 @@ This chain adds one thing. Mark the pull request as a draft as soon as `git-ship
113
119
 
114
120
  - Read `${CLAUDE_SKILL_DIR}/references/draft-mark.md` on reaching that point for the one command that proves the target, marks it, and reads the flag back
115
121
  - A `false` read stops the chain, and never re-issue the mark on it
116
- - Emit the Output block on the wake after `git-ship`'s background CI watch exits on `passing`, not when the turn ends on its watch line
122
+ - Act on the wake after the CI watch exits on `passing`, not when the turn ends on its watch line, moving to Step 7 under a named controller and to the Output block otherwise
117
123
  - Keep `git-ship`'s own verify gate, which repeats Step 3 on a clean run, since a resumed run enters at that gate
118
124
 
125
+ ## Step 7: wait for the reviews
126
+
127
+ Run this step only when the launch named a controlling session, since only a controller dispatches the reviewers. A hand-launched run has nobody reviewing, so it emits the Output block on the CI wake instead of waiting on a pass that cannot arrive.
128
+
129
+ Read review state through `canon pr review-state` alone, since both families post as pull request reviews, and a worker polling issue comments instead stalled with both reviewers idle. Run it in the background with the number Step 6 returned, since the wait outlasts a foreground shell call, and act on the wake.
130
+
131
+ ```bash
132
+ canon pr review-state <number> --wait 1200 --json
133
+ ```
134
+
135
+ Branch on the record's `reason` rather than on the exit, which a shell function wrapping `canon` can flatten to zero.
136
+
137
+ | `reason` | Route |
138
+ | ----------- | ---------------------------------------------------------------------------------------------------------------------- |
139
+ | `covered` | Emit the Output block. Invoke `canon:review-address` when `review.reason` or `ui.reason` reads open at the tip |
140
+ | `timeout` | Send the record to the controller per `canon:role-worker`, emit the Output block, and stop rather than waiting again |
141
+ | Any refusal | Report the `reason` to the controller the same way and stop, since an unread thread is not one nobody has reviewed yet |
142
+
143
+ Skip the `review-address` call when a reviewer's message already started it, since that message carries the same posted pass. A close in both families needs nothing from this session, since the reviewers run the lift on their close.
144
+
119
145
  ## Output
120
146
 
121
147
  Respond with up to two lines:
@@ -19,14 +19,40 @@ gh api graphql -f query='mutation($id: ID!) { convertPullRequestToDraft(input: {
19
19
  gh api repos/{owner}/{repo}/pulls/<number> --jq '{isDraft: .draft}'
20
20
  ```
21
21
 
22
- The target check and the read-back go through REST. The mark itself is a GraphQL mutation, because the REST pull request update carries no draft field, so the mark is local-only: a cloud session's GitHub access blocks GraphQL, the mutation fails there, and the read-back reports `false`. A cloud worker's pull request therefore opens ready, and its chain stops at this step with the pull request already open.
22
+ ## Why the mark is local-only
23
23
 
24
- Name the number `git-ship`'s pull request step returned in every position rather than leaving any to resolve by branch. `${CLAUDE_SKILL_DIR}/../git-pr/REQUIREMENT.md` states why: a lookup that resolves by branch alone can return a closed pull request sharing that head, so the number is resolved once and reused rather than re-derived.
24
+ The target check and the read-back go through REST. The mark itself is a GraphQL mutation, because the REST pull request update carries no draft field.
25
25
 
26
- A refusal is a stop. Report the line the shell printed, then resolve the number from the `number=` and `head=` lines of `git-pr`'s output and re-run the command by hand. The comparison sits in the shell rather than in the session's reading of a printed value, which is the same split `git-pr`'s `assert_own_pr` makes, because the judgment that picked a wrong number is the one that would read the check.
26
+ - A cloud session's GitHub access blocks GraphQL, so the mutation fails there and the read-back reports `false`
27
+ - A cloud worker's pull request therefore opens ready, and its chain stops at this step with the pull request already open
27
28
 
28
- The number crosses from that step's output into a command this session types, which is the one place a number gets authored rather than derived. A number misread there, or inferred from the newest pull request in view, would draft a stranger's pull request. A release pull request is the worst such target, since the mark holds the release and this role may not lift it again.
29
+ ## The number
29
30
 
30
- Report what the read returned rather than what the command printed, since the exit says the call ran and says nothing about the state. A `true` reports a draft. A `false` reports the pull request as opened ready and unsupervised, and the chain stops there. Never re-issue the mark on a disagreeing read, which fights whoever readied it instead of guarding anything.
31
+ Name the number `git-ship`'s pull request step returned in every position rather than leaving any to resolve by branch. `${CLAUDE_SKILL_DIR}/../git-pr/REQUIREMENT.md` states why, since a lookup by branch alone can return a closed pull request sharing that head.
31
32
 
32
- Placement is why the call sits ahead of the watch rather than after it. Marking afterwards leaves the pull request unmarked for the whole CI run, which is the stretch an unattended worker's branch is least supervised. What the mark buys is a reader learning the pull request has had no review yet. It buys no bound on that stretch: readying a pull request to merge lifts the mark, GitHub requires it before a merge, and the act runs through `canon pr lift`, which a reviewer runs on its close and the controlling session runs as the fallback, or through the operator directly. `role-worker` states the mirroring refusal of an instruction to ready the pull request directly, whoever sends it.
33
+ The number crosses from that step's output into a command this session types, the one place a number gets authored rather than derived. A number misread there, or inferred from the newest pull request in view, would draft a stranger's pull request.
34
+
35
+ - A release pull request is the worst such target, since the mark holds the release and this role may not lift it again
36
+
37
+ ### A refusal
38
+
39
+ A refusal is a stop. Report the line the shell printed, then resolve the number from the `number=` and `head=` lines of `git-pr`'s output and re-run the command by hand.
40
+
41
+ The comparison sits in the shell rather than in the session's reading of a printed value, the same split `git-pr`'s `assert_own_pr` makes. The judgment that picked a wrong number is the one that would read the check.
42
+
43
+ ## The read-back
44
+
45
+ Report what the read returned rather than what the command printed, since the exit says the call ran and says nothing about the state.
46
+
47
+ - A `true` reports a draft
48
+ - A `false` reports the pull request as opened ready and unsupervised, and the chain stops there
49
+ - Never re-issue the mark on a disagreeing read, which fights whoever readied it instead of guarding anything
50
+
51
+ ## Why the mark sits ahead of the watch
52
+
53
+ Marking afterwards leaves the pull request unmarked for the whole CI run, the stretch an unattended worker's branch is least supervised. What the mark buys is a reader learning the pull request has had no review yet.
54
+
55
+ It buys no bound on that stretch. Readying a pull request to merge lifts the mark, and GitHub requires it before a merge.
56
+
57
+ - The lift runs through `canon pr lift`, which a reviewer runs on its close and the controlling session runs as the fallback, or through the operator directly
58
+ - `role-worker` states the mirroring refusal of an instruction to ready the pull request directly, whoever sends it
@@ -7,16 +7,18 @@ description: The step an operator resumes from after each auto-ship stop point,
7
7
 
8
8
  Every stop point leaves recoverable state. The user resumes manually from the appropriate step.
9
9
 
10
- | Stop point | Recovery |
11
- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
12
- | No plan (derived) | Run `/plan-feature` to create one |
13
- | No plan (caller-supplied) | Check the path or slug passed to autoship, then re-run |
14
- | No task (caller-supplied) | Check the task path passed to autoship, then re-run |
15
- | Task carries no `Plan:` line | Write the plan, point the task's `Plan:` line at it, then re-run |
16
- | Task points at an archived plan | The work already shipped. Reopen the task against a live plan, or pass that plan's path directly. |
17
- | Task's `Plan:` pointer resolves to nothing | Repoint the task's `Plan:` line at the plan that exists, then re-run |
18
- | Resolved file carries no plan shape | Point autoship at a plan under `.canon/plans/feature-<slug>.md` or at a task under `.canon/tasks/`, then re-run |
19
- | Branch collision on worktree entry | `session-worktree` Step 5 found `<slug>` already as a local branch. Resolve manually (rename or delete the stale branch), then re-run autoship. |
20
- | Verify fails | Read logs, fix manually, run `/git-ship` |
21
- | git-ship fails | Inspect hook or remote error, run again |
22
- | Draft target is not this branch's | Nothing was marked. Read the number and `head=` from `git-pr`'s output, confirm they match, then run the mark by hand |
10
+ | Stop point | Recovery |
11
+ | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
12
+ | No plan (derived) | Run `/plan-feature` to create one |
13
+ | No plan (caller-supplied) | Check the path or slug passed to autoship, then re-run |
14
+ | No task (caller-supplied) | Check the task path passed to autoship, then re-run |
15
+ | Task carries no `Plan:` line | Write the plan, point the task's `Plan:` line at it, then re-run |
16
+ | Task points at an archived plan | The work already shipped. Reopen the task against a live plan, or pass that plan's path directly. |
17
+ | Task's `Plan:` pointer resolves to nothing | Repoint the task's `Plan:` line at the plan that exists, then re-run |
18
+ | Resolved file carries no plan shape | Point autoship at a plan under `.canon/plans/feature-<slug>.md` or at a task under `.canon/tasks/`, then re-run |
19
+ | Branch collision on worktree entry | `session-worktree` Step 5 found `<slug>` already as a local branch. Resolve manually (rename or delete the stale branch), then re-run autoship. |
20
+ | Verify fails | Read logs, fix manually, run `/git-ship` |
21
+ | git-ship fails | Inspect hook or remote error, run again |
22
+ | Draft target is not this branch's | Nothing was marked. Read the number and `head=` from `git-pr`'s output, confirm they match, then run the mark by hand |
23
+ | Review wait timed out | The record went to the controller. Once the reviewers are dispatched, re-run `canon pr review-state <number> --wait 1200 --json` in the background |
24
+ | Review wait refused | Fix what the `reason` names, such as an unreadable remote, then re-run `canon pr review-state <number> --wait 1200 --json` in the background |
@@ -0,0 +1,59 @@
1
+ ---
2
+ title: Fan-in member exit and integrator merge
3
+ description: How auto-ship stops a fan-in member after its push and how an integrator merges the member branches before its own build, so a set of parallel batches ships as one pull request
4
+ ---
5
+
6
+ # Fan-in
7
+
8
+ The session reads this file at one of two points in `auto-ship`. A member reads `## Member exit` after Step 4, and an integrator reads `## Integrator merge` at the opening of Step 2. When to fan in and how a plan declares its side are in `${CLAUDE_SKILL_DIR}/../plan-feature/references/batches.md`.
9
+
10
+ ## Member exit
11
+
12
+ Step 3's verify and Step 4's test order have already run, so the branch is green and committed. The member ends here rather than at a pull request.
13
+
14
+ 1. Push the branch with `git push -u origin <branch>`. The pre-push hook runs the project check, and a member that fails it stops the way Step 3 does.
15
+ 2. Read the head with `git rev-parse HEAD` once the push returns.
16
+ 3. Announce the batch done to the controlling session through `canon:session-relay`, carrying the branch, the head commit, the integrator the plan names, and any row this build turned up.
17
+ 4. Stop. Run no UI checklist, no `git-ship`, and no draft mark.
18
+
19
+ The pushed branch is the done signal the integrator's gate reads, so push once, at the end, and never mid-build. The announcement carries no reach, since members of one set share files by design and the integrator's announcement carries the reach for the whole set.
20
+
21
+ A fix the integrator needs after this point lands on the integrator's branch, since a member has no pull request to fix it on.
22
+
23
+ ## Integrator merge
24
+
25
+ The members are pushed before this run starts, since `canon tasks plan-answers` holds an integrator until every member branch is on origin. Confirm that before merging anything:
26
+
27
+ ```bash
28
+ git fetch origin --quiet
29
+ canon tasks plan-answers <plan> --json
30
+ ```
31
+
32
+ Stop on `launchable: false`, reporting each `open` entry, since a `Fan-in pending` entry names a member that has not pushed and a `Fan-in set` entry names a set whose sides disagree. Then merge each member in the order the `Fans in` bullet lists:
33
+
34
+ ```bash
35
+ git merge --no-ff --no-edit origin/<member branch>
36
+ ```
37
+
38
+ ### Conflicts and the order
39
+
40
+ `--no-ff` keeps one merge commit per member, so `git log --first-parent` shows the reviewer one member at a time. Never squash or fast-forward a member in.
41
+
42
+ - Resolve each conflict by `## Resolving a conflict` in `${CLAUDE_SKILL_DIR}/../review-address/references/rebase-conflicts.md`
43
+ - Stop on a hunk needing a decision with `git merge --abort` in place of the rebase abort that file names
44
+ - Regenerate the indexes once, after the last merge, rather than resolving a generated file by hand at each one
45
+ - Run `git config rerere.enabled true` before the first merge, since a stale integrator rebases with `--rebase-merges` per the same file and that replays each merge without its recorded resolution
46
+
47
+ Continue Step 2 with the integrator plan's own `**Files to touch:**`, then the rest of the chain as an ordinary worker.
48
+
49
+ ### What the pull request carries
50
+
51
+ Say in the pull request body that each member is one merge commit and name the members in merge order. Once the pull request merges, delete each member's branch on origin, its worktree, and its local branch:
52
+
53
+ ```bash
54
+ git push origin --delete <member branch>
55
+ git worktree remove <member worktree>
56
+ git branch -D <member branch>
57
+ ```
58
+
59
+ Read each member's worktree path off `git worktree list --porcelain` by its branch. The integrator holds the member list, so the deletion is its step rather than a sweep's. No member opens a pull request, so `canon worktrees reclaim` never reads a member's branch as merged and reports a leftover worktree as unmerged work on every run.
@@ -9,21 +9,42 @@ Step 1 of `auto-ship`, tier 1. The session reads this file when the invocation c
9
9
 
10
10
  ## Reading the pointer
11
11
 
12
- One plan per task is what makes the tier 1 read unambiguous, so it takes the first `Plan:` line outside a fenced sample and never scans for a second line or reads past a second link on that one. A caller who means the task holds its path already, having read it off the board, which is why a bare slug never reaches this tier.
12
+ One plan per task is what makes the tier 1 read unambiguous. It takes the first `Plan:` line outside a fenced sample, and never scans for a second line or reads past a second link on that one.
13
13
 
14
- Read the target out of the link's parentheses, and take the rest of the line when the line carries no link, since an older task writes the target as a plain path with nothing around it. A bare line counts its leading run of tokens carrying `/` or ending in `.md` as targets and stops at the first word that is neither, so trailing prose after one path still names a single plan.
14
+ - A caller who means the task already holds its path, having read it off the board, which is why a bare slug never reaches this tier
15
+ - Read the target out of the link's parentheses, and take the rest of the line when it carries no link, since an older task writes the target as a plain path
16
+ - A bare line counts its leading run of tokens carrying `/` or ending in `.md` as targets and stops at the first word that is neither, so trailing prose after one path still names a single plan
15
17
 
16
- Resolve a relative target against the directory holding the task file rather than against `.canon/tasks/`, and take a project-root target from the root. The archived task is what makes that base matter, since the standard points its line at `../../plans/archive/feature-<slug>.md` once the task sits a folder deeper, and reading that from `.canon/tasks/` lands on a repository-root `plans/archive/` that never exists.
18
+ ### Resolving the target
19
+
20
+ Resolve a relative target against the directory holding the task file rather than against `.canon/tasks/`, and take a project-root target from the root.
21
+
22
+ The archived task is what makes that base matter. The standard points its line at `../../plans/archive/feature-<slug>.md` once the task sits a folder deeper, and reading that from `.canon/tasks/` lands on a repository-root `plans/archive/` that never exists.
17
23
 
18
24
  ## When a tier fails
19
25
 
20
- Tier 1 stops on four failures, and each names a different repair:
26
+ Tier 1 stops on four failures, each naming a different repair. Test them in the order below.
27
+
28
+ | Failure | Stop |
29
+ | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
30
+ | The line links several plans | `❌ <path> links <N> plans on its Plan: line (<every target>), so the task does not say which one this run builds. Pass one plan path directly.` |
31
+ | No `Plan:` line at all | `❌ <path> carries no Plan: line, so nothing there names a plan to run. Write the plan and point the task at it, or pass the plan path directly.` |
32
+ | Pointer into a plans archive | `❌ <path> points at an archived plan, which describes work that already shipped. Reopen the task against a live plan, or pass that plan directly.` |
33
+ | Pointer resolves to no file | `❌ <path> points at <target>, which does not exist. The citation is stale, so repoint the task or pass the plan path directly.` |
34
+
35
+ ### Why each test sits where it does
36
+
37
+ Each row carries a reason a shortcut would get wrong.
38
+
39
+ - Test several links first, since taking the first link runs whichever plan leads the line and reports success
40
+ - A row still awaiting a plan is the ordinary case, so the no-line message names the missing pointer rather than the plan sections a reader would then hunt for
41
+ - Test the resolved path for an archive rather than the task's outcomes or its `Pull request:` line, since a stale board gets its ticks wrong and the standard fixes where a shipped pointer lands
42
+
43
+ ### What counts as an archive
21
44
 
22
- - The line links more than one plan. Stop: `❌ <path> links <N> plans on its Plan: line (<every target>), so the task does not say which one this run builds. Pass one plan path directly.` Test this ahead of the three below, since taking the first link runs whichever plan leads the line and reports success.
23
- - No `Plan:` line at all. Stop: `❌ <path> carries no Plan: line, so nothing there names a plan to run. Write the plan and point the task at it, or pass the plan path directly.` A row still awaiting a plan is the ordinary case, so the message names the missing pointer rather than the missing plan sections a reader would then go hunting for.
24
- - The pointer resolves into a plans archive. Stop: `❌ <path> points at an archived plan, which describes work that already shipped. Reopen the task against a live plan, or pass that plan directly.` Test the resolved path rather than the task's outcomes or its `Pull request:` line, since a stale board gets its ticks wrong and the standard fixes where a shipped pointer lands.
25
- - The pointer resolves to no file. Stop: `❌ <path> points at <target>, which does not exist. The citation is stale, so repoint the task or pass the plan path directly.`
45
+ An archive is `.canon/plans/archive/`, plus the two older spellings `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md` leaves in place for a project that archived plans before the folder nested. From a task those read `../plans-archive/` and `../.tmp/plans-archive/`.
26
46
 
27
- An archive is `.canon/plans/archive/` and also the two older spellings `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md` leaves in place for a project that archived plans before the folder nested, written from a task as `../plans-archive/` and `../.tmp/plans-archive/`. Test all three, since a shipped pointer in a project nobody migrated lands on the older two. Run the archive test ahead of the existence test, so a pointer into an archive that no longer holds the file still refuses as shipped work rather than as a stale citation.
47
+ - Test all three, since a shipped pointer in a project nobody migrated lands on the older two
48
+ - Run the archive test ahead of the existence test, so a pointer into an archive that no longer holds the file still refuses as shipped work rather than as a stale citation
28
49
 
29
- Each tier fails for a different reason and says so. A supplied path resolving to nothing is a typo, a derived path resolving to nothing is a plan nobody wrote, and a task pointer resolving to nothing is a stale citation the board should have caught.
50
+ Each tier fails for a different reason and says so. A supplied path resolving to nothing is a typo, a derived one is a plan nobody wrote, and a task pointer resolving to nothing is a stale citation the board should have caught.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: UI checklist counts
3
- description: What auto-ship Step 5 holds when ui-checklist produces a checklist, so the closing block can name the unchecked visual boxes after git-pr has removed the file
3
+ description: What auto-ship Step 5 holds when ui-checklist produces a checklist, so the closing block can name the unchecked visual boxes after pr-evidence has removed the file
4
4
  ---
5
5
 
6
6
  # Hold the checklist counts
@@ -9,13 +9,18 @@ Step 5 of `auto-ship`. The session reads this file when `ui-checklist` has produ
9
9
 
10
10
  The chain continues to Step 6 here in every run, dispatched or hand-run, since the skill cannot tell the two apart and the draft mark at Step 6 holds the merge either way.
11
11
 
12
+ ## What to count
13
+
12
14
  Count two things in `.canon/tmp/handoff/ui-checklist/<slug>.md` at the main worktree root:
13
15
 
14
16
  - `<N>`, the `- [ ]` lines
15
17
  - `<M>`, how many of those end in `(taste)`
16
18
 
17
- Hold both until the Output block. `git-pr` removes the file once it posts the evidence comment, so a count taken after Step 6 reads nothing.
19
+ Hold both until the Output block. `pr-evidence` removes the file once it posts the evidence comment, so a count taken after Step 6 reads nothing.
20
+
21
+ ## Reading the counts into the closing block
18
22
 
19
- The Output block's second line takes both counts. When every box is a taste box, `<N>` and `<M>` match and the boxes are owed to the operator alone, since a driver reports a taste box as needing a person's eyes rather than passing it, so say the operator and drop the UI reviewer clause.
23
+ The Output block's second line takes both counts. When every box is a taste box, `<N>` and `<M>` match and the boxes are owed to the operator alone, so say the operator and drop the UI reviewer clause.
20
24
 
21
- When `ui-checklist` reports nothing to verify visually, it writes no file, Step 5 holds no count, and the Output block carries no boxes line.
25
+ - A driver reports a taste box as needing a person's eyes rather than passing it, which is why the operator owns an all-taste set
26
+ - When `ui-checklist` reports nothing to verify visually, it writes no file, Step 5 holds no count, and the Output block carries no boxes line
@@ -13,9 +13,15 @@ This step runs ahead of Step 1, so what it holds is the raw invocation argument
13
13
  canon tasks plan-branch <argument> --json
14
14
  ```
15
15
 
16
- - `conforms: true`: pass the record's `branch`, which is already `<type>/<slug>`, and invoke nothing else to derive a name.
17
- - `conforms: false`: the plan's own filename breaks a cap in `${CLAUDE_SKILL_DIR}/../git-branch/references/branch.md`. Pass `branch` anyway and say the cap it broke, since the alternative is a name this session shortened by hand, which is a second derivation and the thing this call exists to prevent. `git-branch` decides the rename at ship.
18
- - Anything else, including a refusal, a record carrying no `branch` key, and an installed binary carrying no `plan-branch` subcommand: invoke `canon:session-worktree` bare and let its ladder derive the name. Say the verb did not answer, so a reader can tell a derived name from a fallback one.
16
+ | Record | Route |
17
+ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
18
+ | `conforms: true` | Pass the record's `branch`, already `<type>/<slug>`, and invoke nothing else to derive a name |
19
+ | `conforms: false` | The plan's filename breaks a cap in `${CLAUDE_SKILL_DIR}/../git-branch/references/branch.md`. Pass `branch` anyway and say the cap it broke |
20
+ | Anything else | Invoke `canon:session-worktree` bare and let its ladder derive the name, saying the verb did not answer |
21
+
22
+ - On `conforms: false`, the alternative is a name this session shortened by hand, a second derivation and the thing this call exists to prevent. `git-branch` decides the rename at ship.
23
+ - Anything else covers a refusal, a record carrying no `branch` key, and an installed binary carrying no `plan-branch` subcommand
24
+ - Saying the verb did not answer lets a reader tell a derived name from a fallback one
19
25
 
20
26
  The dispatch runbook runs the same verb on the same plan to pick the branch its collision check clears, so calling it here is what makes the checked branch and the taken branch one string.
21
27