@erclx/canon 5.37.0 → 5.39.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 (197) hide show
  1. package/README.md +1 -1
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/SKILL.md +12 -9
  4. package/claude/skills/auto-ship/SKILL.md +43 -23
  5. package/claude/skills/backlog-triage/SKILL.md +62 -53
  6. package/claude/skills/backlog-triage/references/apply.md +69 -0
  7. package/claude/skills/build-in-slices/SKILL.md +8 -4
  8. package/claude/skills/canon-cli/SKILL.md +5 -1
  9. package/claude/skills/canon-feedback/SKILL.md +18 -8
  10. package/claude/skills/canon-feedback-triage/SKILL.md +10 -3
  11. package/claude/skills/canvas-board/SKILL.md +36 -17
  12. package/claude/skills/ci-workflow/SKILL.md +5 -4
  13. package/claude/skills/code-craft/SKILL.md +10 -6
  14. package/claude/skills/codebase-layout/SKILL.md +3 -5
  15. package/claude/skills/context-fold/REQUIREMENT.md +4 -4
  16. package/claude/skills/context-fold/SKILL.md +42 -23
  17. package/claude/skills/context-fold/references/context-refresh.md +4 -3
  18. package/claude/skills/context-fold/references/context.md +79 -64
  19. package/claude/skills/create-rule/SKILL.md +7 -3
  20. package/claude/skills/create-skill/SKILL.md +27 -7
  21. package/claude/skills/create-skill/references/skill-reference.md +41 -17
  22. package/claude/skills/decision-escalate/SKILL.md +8 -5
  23. package/claude/skills/deploy-app/SKILL.md +1 -1
  24. package/claude/skills/deprecation-migration/SKILL.md +12 -4
  25. package/claude/skills/design-taste/SKILL.md +17 -11
  26. package/claude/skills/docs-sync/REQUIREMENT.md +1 -0
  27. package/claude/skills/docs-sync/SKILL.md +17 -6
  28. package/claude/skills/docs-sync/references/docs.md +54 -21
  29. package/claude/skills/docs-sync/references/readme.md +32 -8
  30. package/claude/skills/document-health/SKILL.md +3 -4
  31. package/claude/skills/draft-and-pick/SKILL.md +73 -47
  32. package/claude/skills/draft-doc/SKILL.md +9 -4
  33. package/claude/skills/draft-doc/references/context.md +4 -4
  34. package/claude/skills/draft-doc/references/docs.md +1 -0
  35. package/claude/skills/draft-doc/references/wiki.md +3 -2
  36. package/claude/skills/draft-figure/SKILL.md +9 -7
  37. package/claude/skills/draft-identity/SKILL.md +24 -52
  38. package/claude/skills/draft-identity/references/card-route.md +46 -0
  39. package/claude/skills/draft-ready/SKILL.md +19 -7
  40. package/claude/skills/draft-screencast/SKILL.md +6 -112
  41. package/claude/skills/draft-screencast/references/beats-template.md +119 -0
  42. package/claude/skills/draft-slides/SKILL.md +7 -4
  43. package/claude/skills/git-branch/SKILL.md +3 -3
  44. package/claude/skills/git-commit/SKILL.md +5 -3
  45. package/claude/skills/git-followup/SKILL.md +16 -9
  46. package/claude/skills/git-issue/SKILL.md +6 -5
  47. package/claude/skills/git-issue/references/issue.md +11 -10
  48. package/claude/skills/git-pr/SKILL.md +56 -44
  49. package/claude/skills/git-ship/SKILL.md +43 -34
  50. package/claude/skills/git-ship/references/ci-watch.md +31 -0
  51. package/claude/skills/git-split/SKILL.md +28 -100
  52. package/claude/skills/git-split/references/independent.md +40 -0
  53. package/claude/skills/git-split/references/stacked.md +73 -0
  54. package/claude/skills/git-stage/SKILL.md +9 -7
  55. package/claude/skills/git-worktree/SKILL.md +27 -49
  56. package/claude/skills/git-worktree/references/cleanup.md +55 -0
  57. package/claude/skills/markdown-craft/SKILL.md +2 -2
  58. package/claude/skills/markdown-craft/references/markdown.md +82 -52
  59. package/claude/skills/plan-feature/SKILL.md +52 -46
  60. package/claude/skills/plan-feature/references/batches.md +46 -0
  61. package/claude/skills/plan-groundwork/SKILL.md +60 -74
  62. package/claude/skills/plan-groundwork/references/groundwork.md +53 -24
  63. package/claude/skills/plan-groundwork/references/output.md +48 -0
  64. package/claude/skills/plan-intake/SKILL.md +28 -21
  65. package/claude/skills/plan-intake-answer/SKILL.md +14 -13
  66. package/claude/skills/read-frames/SKILL.md +2 -0
  67. package/claude/skills/record-screencast/SKILL.md +31 -11
  68. package/claude/skills/review-address/SKILL.md +76 -158
  69. package/claude/skills/review-address/references/rebase-only.md +34 -0
  70. package/claude/skills/review-craft/SKILL.md +27 -11
  71. package/claude/skills/review-pr/SKILL.md +91 -40
  72. package/claude/skills/review-pr/references/post.md +1 -1
  73. package/claude/skills/review-ui/SKILL.md +62 -37
  74. package/claude/skills/review-ui/references/hosted-preview.md +30 -0
  75. package/claude/skills/role-orchestrator/SKILL.md +105 -74
  76. package/claude/skills/role-orchestrator/references/orchestrator-filing.md +26 -0
  77. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +1 -1
  78. package/claude/skills/role-planner/SKILL.md +66 -64
  79. package/claude/skills/role-reviewer/SKILL.md +57 -46
  80. package/claude/skills/role-worker/SKILL.md +68 -63
  81. package/claude/skills/search-craft/SKILL.md +7 -5
  82. package/claude/skills/session-compact/SKILL.md +16 -11
  83. package/claude/skills/session-map/SKILL.md +31 -10
  84. package/claude/skills/session-relay/SKILL.md +32 -65
  85. package/claude/skills/session-resume/SKILL.md +18 -13
  86. package/claude/skills/session-worktree/SKILL.md +70 -42
  87. package/claude/skills/systematic-debugging/SKILL.md +6 -1
  88. package/claude/skills/target-check/SKILL.md +13 -6
  89. package/claude/skills/target-setup/SKILL.md +41 -30
  90. package/claude/skills/target-setup/references/verify.md +6 -5
  91. package/claude/skills/task-board/SKILL.md +68 -60
  92. package/claude/skills/task-board/references/create-report.md +36 -0
  93. package/claude/skills/task-board/references/tasks.md +3 -1
  94. package/claude/skills/task-complete/SKILL.md +20 -18
  95. package/claude/skills/teach-workspace/SKILL.md +61 -33
  96. package/claude/skills/test-craft/SKILL.md +2 -1
  97. package/claude/skills/test-first/SKILL.md +15 -9
  98. package/claude/skills/ui-checklist/SKILL.md +18 -16
  99. package/claude/skills/ux-audit/SKILL.md +9 -10
  100. package/claude/skills/ux-measure/SKILL.md +22 -18
  101. package/claude/skills/video-craft/SKILL.md +15 -7
  102. package/claude/skills/write-human/SKILL.md +9 -1
  103. package/claude/skills/youtube-transcripts/SKILL.md +2 -1
  104. package/docs/agents/context-audit-checks.md +19 -1
  105. package/docs/agents/context-audit.md +2 -2
  106. package/docs/agents/driver.md +1 -1
  107. package/docs/agents/hooks.md +21 -1
  108. package/docs/agents/index.md +10 -14
  109. package/docs/agents/indexes.md +25 -29
  110. package/docs/agents/install-and-sync.md +124 -235
  111. package/docs/agents/markdown-audit-density.md +39 -0
  112. package/docs/agents/markdown-audit-weight.md +9 -7
  113. package/docs/agents/markdown-audit.md +18 -14
  114. package/docs/agents/migrate.md +41 -16
  115. package/docs/agents/output-shape.md +60 -45
  116. package/docs/agents/overview.md +36 -12
  117. package/docs/agents/routing.md +1 -1
  118. package/docs/agents/rule-citations.md +1 -1
  119. package/docs/agents/skills-reach.md +1 -1
  120. package/docs/agents/targets.md +25 -77
  121. package/docs/target/migrations.md +1 -1
  122. package/docs/target/projects.md +3 -3
  123. package/docs/target/scenarios.md +1 -1
  124. package/docs/workflow/ai-workflow.md +2 -2
  125. package/docs/workflow/parallel-features.md +2 -2
  126. package/docs/workflow/skill-map.md +1 -1
  127. package/docs/workflow/visual-design-workflow.md +1 -1
  128. package/governance/detect.toml +6 -1
  129. package/package.json +6 -3
  130. package/scripts/core/install-check.sh +1 -1
  131. package/src/commands/claude.ts +1 -2
  132. package/src/commands/context.ts +106 -7
  133. package/src/commands/hooks.ts +63 -0
  134. package/src/commands/markdown.ts +169 -32
  135. package/src/commands/pr.ts +1 -1
  136. package/src/commands/records.ts +0 -1
  137. package/src/context/audit.ts +256 -5
  138. package/src/context/classify/prompts.ts +1 -1
  139. package/src/context/gate.ts +23 -7
  140. package/src/context/shape-gated.ts +28 -0
  141. package/src/design/document.ts +1 -1
  142. package/src/design/tokens.ts +1 -1
  143. package/src/docs/read.ts +15 -21
  144. package/src/gate/measures.ts +15 -190
  145. package/src/gate/stages.ts +21 -23
  146. package/src/hooks/reply-links.ts +110 -0
  147. package/src/markdown/density.ts +258 -0
  148. package/src/markdown/gate.ts +57 -18
  149. package/src/markdown/reference-shape.ts +103 -0
  150. package/src/markdown/scope.ts +174 -0
  151. package/src/markdown/structure.ts +10 -60
  152. package/src/sandbox/equivalence.ts +7 -9
  153. package/src/shipped/references.ts +1 -1
  154. package/src/teach/browser/head.ts +2 -2
  155. package/src/teach/browser/sidebar.ts +3 -3
  156. package/src/teach/browser/theme.ts +2 -2
  157. package/src/tooling/inject.ts +2 -2
  158. package/src/tooling/package.ts +15 -18
  159. package/src/tooling/verify-stack.ts +6 -3
  160. package/tooling/astro/configs/.oxfmtrc.json +8 -0
  161. package/tooling/astro/configs/.prettierrc +13 -0
  162. package/tooling/astro/manifest.toml +13 -0
  163. package/tooling/astro/reference.md +15 -5
  164. package/tooling/base/configs/.oxfmtrc.json +7 -0
  165. package/tooling/base/configs/scripts/verify.ts +1 -1
  166. package/tooling/base/manifest.toml +6 -6
  167. package/tooling/base/reference.md +12 -9
  168. package/tooling/base/seeds/.lintstagedrc +1 -3
  169. package/tooling/base/seeds/canon/context/ci.md +3 -3
  170. package/tooling/base/seeds/canon/context/development.md +2 -2
  171. package/tooling/claude/manifest.toml +5 -1
  172. package/tooling/claude/seeds/.claude/hooks/reply-links.sh +72 -0
  173. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +22 -1
  174. package/tooling/claude/seeds/.claude/settings.json +4 -0
  175. package/tooling/go/reference.md +2 -2
  176. package/tooling/nextjs/configs/.oxlintrc.json +223 -0
  177. package/tooling/nextjs/manifest.toml +2 -5
  178. package/tooling/nextjs/reference.md +4 -4
  179. package/tooling/php/manifest.toml +6 -1
  180. package/tooling/php/reference.md +2 -2
  181. package/tooling/python/manifest.toml +6 -1
  182. package/tooling/python/reference.md +4 -4
  183. package/tooling/vite-react/manifest.toml +1 -4
  184. package/tooling/vite-react/reference.md +3 -3
  185. package/tooling/web/configs/.oxlintrc.json +213 -0
  186. package/tooling/web/manifest.toml +9 -13
  187. package/tooling/web/reference.md +14 -14
  188. package/docs/agents/commands.md +0 -154
  189. package/docs/agents/docs.md +0 -27
  190. package/docs/agents/drift-surfaces.md +0 -201
  191. package/docs/agents/gov-detect.md +0 -47
  192. package/docs/agents/scripting.md +0 -142
  193. package/src/markdown/growth.ts +0 -43
  194. package/tooling/base/configs/.prettierrc +0 -12
  195. package/tooling/base/seeds/.prettierignore +0 -0
  196. package/tooling/nextjs/configs/eslint.config.js +0 -91
  197. package/tooling/web/configs/eslint.config.js +0 -82
@@ -37,33 +37,41 @@ Every phase runs under `CANON_NON_INTERACTIVE=1` with no TTY. Three phases
37
37
  otherwise pause, and each takes an argument that answers its prompt ahead of
38
38
  time:
39
39
 
40
- - `indexes` confirms its folder list with the operator. `setup indexes --all`
41
- takes every candidate the scan found, and `--folders <a>,<b>` takes a named
42
- set. With neither, and with no TTY, the phase reports its candidates and stops
43
- rather than choosing for the operator.
44
- - `install` and `gov` confirm the proposed extras with the operator.
45
- `--extras <a>,<b>` takes a named set and `--extras none` takes none. With
46
- neither, and with nobody to answer, both take every proposal and list each in
47
- the report rather than stopping.
48
- - `gov` stops on a detected technology with no matching rule. `--skip-gaps`
49
- proceeds with the matched layer and lists the gap in the report.
40
+ | Phase | Pauses to | Answered ahead by | With neither and no TTY |
41
+ | ------------------- | ------------------------------------------ | ---------------------------------------------------------- | ----------------------------------------------------- |
42
+ | `indexes` | Confirm its folder list | `--all` for every candidate, `--folders <a>,<b>` for a set | Reports its candidates and stops rather than choosing |
43
+ | `install` and `gov` | Confirm the proposed extras | `--extras <a>,<b>` for a set, `--extras none` for none | Takes every proposal and lists each in the report |
44
+ | `gov` | Stop on a detected technology with no rule | `--skip-gaps`, which proceeds and lists the gap | Stops on the gap |
50
45
 
51
46
  Claude Code's tool permission dialog is the confirmation gate for everything
52
47
  else. Do not pause for a separate confirmation beyond the extras question.
53
48
 
54
49
  ## Scope
55
50
 
56
- - This skill and `canon init` run once on a fresh scaffold, never on an existing project. They do not guard against clobbering existing configs. When tempted to add guards, mode switches, or an existing-project branch, stop. Extend the per-domain `canon <domain> install` or `canon sync` paths instead.
51
+ - This skill and `canon init` run once on a fresh scaffold, never on an existing project, and do not guard against clobbering existing configs
52
+ - Add no guard, mode switch, or existing-project branch here. Extend the per-domain `canon <domain> install` or `canon sync` paths instead.
57
53
  - The chain stops at the project edge. `git-commit` also ships, reaching the project's history, and does not run here.
58
- - Claude Code plugins are a machine concern rather than a project one, so no phase here installs them. Install them with `claude plugin install <name> --scope user`, which applies in every project on the machine and is copied into none.
54
+ - Claude Code plugins are a machine concern, so no phase installs them. `claude plugin install <name> --scope user` applies in every project on the machine and copies into none.
59
55
 
60
56
  ## Declined states
61
57
 
62
- Three states reach this skill that the chain does not serve. Name the destination for each, so the refusal routes rather than ends. The first two stop the chain outright and the third runs it on a default the person may not want.
58
+ Three states reach this skill that the chain does not serve. Name the destination for each, so the refusal routes rather than ends.
63
59
 
64
- - **An existing project.** Stop and hand off to `target-check`, which reports what the target already carries before anything is repaired. This chain never reads that, so any per-domain install picked here is a guess against configs nobody read. The Scope bullet above names the same commands as the authoring alternative, and this is the destination a person takes.
65
- - **An install wanting the `.claude/` folder alone.** Stop. Run `canon claude init` for the seed docs, then run the `indexes` phase over the project's own documentation folders. Neither needs the tooling sync the `install` phase runs.
66
- - **A language the toolkit carries no stack for.** The chain still runs, on `base`, with the fallback marked in the preview. A project that wants none of what `base` carries declines there and takes the `gov` phase, which is language-neutral and installs rules without the `base` development dependencies the tooling layer would drop on it. Say so at the preview rather than resolving it here, since the fallback is a working default and only the person can say whether it fits.
60
+ ### An existing project
61
+
62
+ Stop and hand off to `target-check`, which reports what the target already carries before anything is repaired. This chain never reads that, so a per-domain install picked here is a guess against configs nobody read.
63
+
64
+ ### The `.claude/` folder alone
65
+
66
+ Stop. Run `canon claude init` for the seed docs, then the `indexes` phase over the project's own documentation folders. Neither needs the tooling sync the `install` phase runs.
67
+
68
+ ### A language with no stack
69
+
70
+ The chain still runs, on `base`, with the fallback marked in the preview. A project wanting none of `base` declines there and takes the `gov` phase, which is language-neutral and drops no `base` development dependencies.
71
+
72
+ Say so at the preview rather than resolving it here, since the fallback is a working default and only the person can say whether it fits.
73
+
74
+ ### Route rather than build
67
75
 
68
76
  Do not add a stack, a mode switch, or an existing-project branch to satisfy one of these. Each destination already exists and routing to it costs a line.
69
77
 
@@ -91,32 +99,38 @@ CANON_NON_INTERACTIVE=1 canon init \
91
99
 
92
100
  Omit any flag whose resolved value is empty.
93
101
 
94
- Step 2: `canon tooling sync <tooling-stack> --write` installs stack deps, scripts, gitignore entries, seeds, golden configs, and drops the reference doc. The extends chain is walked, so syncing `vite-react` also pulls `web` and `base` configs. Skip if the tooling stack is `base` (already synced by `canon init`).
102
+ Step 2: `canon tooling sync <tooling-stack> --write` installs stack deps, scripts, gitignore entries, seeds, golden configs, and the reference doc. It walks the extends chain, so `vite-react` also pulls `web` and `base`.
95
103
 
96
- `--write` is required. A headless run without it reports what it would replace and exits 1, since golden configs overwrite whatever the target holds at those paths. Scaffolding into a fresh target has nothing to lose, so pass it directly rather than reading a report first.
104
+ - Skip it if the tooling stack is `base`, which `canon init` already synced
105
+ - `--write` is required. Without it a headless run reports what it would replace and exits 1, since golden configs overwrite the target's files.
106
+ - A fresh target has nothing to lose, so pass `--write` directly rather than reading a report first
97
107
 
98
108
  ```bash
99
109
  CANON_NON_INTERACTIVE=1 canon tooling sync <tooling-stack> <target> --write
100
110
  ```
101
111
 
102
- Monorepo with multiple language roots: run `canon init` once at the repo root so `base` (husky, prettier, commitlint, CI) lands single, then sync each subtree with `--skip base` so the shared layer is not re-dropped. Take the subtree commands from the per-root rows the preview printed rather than picking folders by hand. Detection found each root and its stack, and the two commands below show the shape.
112
+ ### Monorepo with multiple language roots
113
+
114
+ Run `canon init` once at the repo root so `base` (husky, oxfmt, commitlint, CI) lands once, then sync each subtree with `--skip base`. Take the subtree commands from the preview's per-root rows rather than picking folders by hand.
103
115
 
104
116
  ```bash
105
117
  CANON_NON_INTERACTIVE=1 canon tooling sync vite-react ./frontend --skip base --write
106
118
  CANON_NON_INTERACTIVE=1 canon tooling sync python ./backend --skip base --write
107
119
  ```
108
120
 
109
- Without `--skip base`, each subtree re-drops husky, and git honors only one `core.hooksPath`, so the extra hook dirs silently break. Each subtree keeps its own framework configs, and its own stack reference reads through `canon tooling reference <stack>`.
121
+ - Without `--skip base`, each subtree re-drops husky, and git honors only one `core.hooksPath`, so the extra hook dirs silently break
122
+ - Each subtree keeps its own framework configs, and its stack reference reads through `canon tooling reference <stack>`
123
+ - A subtree sync withholds `.github/`, since GitHub reads workflows only at the root, and names each withheld file with the `working-directory` a root CI job needs
124
+ - A subtree needs its own `bun init` before its scripts and dependencies land, and the sync warns and skips them until then
110
125
 
111
- A subtree sync withholds `.github/`, since GitHub reads workflows only at the repository root, and names each withheld file with the `working-directory` a root CI job needs. A subtree needs its own `bun init` before its scripts and dependencies land. The sync warns and skips them until then.
126
+ ### Post-sync fixups
112
127
 
113
128
  Step 3: post-sync fixups. Golden configs arrive from sync, so no config generation is required. But a few items may need a one-time touch:
114
129
 
115
- - **ESLint version pin.** If `bun create vite` installed `eslint@^10` and the manifest pins `eslint@^9`, sync does not override a present dep. Run `bun add -d eslint@^9` if `bun run lint:fix` fails with `Class extends value undefined`.
116
130
  - **File naming.** `bun create vite`'s `App.tsx` violates the `KEBAB_CASE` rule. Rename to `app.tsx` and update the import in `main.tsx`.
117
131
  - **Docs.** Run `canon tooling reference <tooling-stack>` and `canon tooling reference web` for any stack-specific follow-ups (Chrome extension overrides, setup script details).
118
132
 
119
- Do not generate ESLint, Vitest, or Playwright configs. They ship as golden files. Generating from prose duplicates what sync already installed.
133
+ Do not generate oxlint, ESLint, Vitest, or Playwright configs. They ship as golden files. Generating from prose duplicates what sync already installed.
120
134
 
121
135
  ## Phase: gov
122
136
 
@@ -148,12 +162,9 @@ Read `${CLAUDE_SKILL_DIR}/references/indexes.md`. It holds the scan, the
148
162
  frontmatter drafting, the scaffold, the validate step, and the convention seed
149
163
  offer.
150
164
 
151
- This phase confirms candidate folders with the operator rather than running
152
- unattended, which is the one phase in the chain that pauses for a conversation
153
- unless `--all` or `--folders` answered it ahead of time. A fresh scaffold usually
154
- has no markdown-heavy folder yet, so this phase frequently finds nothing to
155
- present, which its reference defines as its own outcome rather than a failure of
156
- the chain.
165
+ This is the one phase that pauses to confirm candidate folders with the
166
+ operator, unless `--all` or `--folders` answered it ahead of time. A fresh scaffold usually has no markdown-heavy folder yet, so this phase often
167
+ finds nothing, which its reference defines as an outcome rather than a failure.
157
168
 
158
169
  ## Report
159
170
 
@@ -161,7 +172,7 @@ After the chain, report:
161
172
 
162
173
  - Domains installed, with a check per domain
163
174
  - Tooling stack synced, or skipped. Name the layers pulled via the extends chain.
164
- - Any post-sync fixups applied (ESLint pin, filename renames)
175
+ - Any post-sync fixups applied (filename renames)
165
176
  - `verify` outcome, naming the depth it ran at
166
177
 
167
178
  ### Outcomes and gaps
@@ -58,10 +58,11 @@ exit code is the whole verdict.
58
58
  | Order | Script | Skip if |
59
59
  | ----- | ----------- | --------------------------- |
60
60
  | 1 | `lint:fix` | absent |
61
- | 2 | `typecheck` | absent |
62
- | 3 | `check` | absent |
63
- | 4 | `test:run` | absent, fall back to `test` |
64
- | 5 | `build` | absent |
61
+ | 2 | `format` | absent |
62
+ | 3 | `typecheck` | absent |
63
+ | 4 | `check` | absent |
64
+ | 5 | `test:run` | absent, fall back to `test` |
65
+ | 6 | `build` | absent |
65
66
 
66
67
  Do not run composite scripts like `check:full`. Do not run `dev`, `preview`,
67
68
  `test:e2e`, `test:ui`, or `screenshot` at this depth. They belong to `deep`
@@ -80,7 +81,7 @@ server, and whether the screenshot harness produces output.
80
81
  | 3 | `test:e2e` | absent | leaf script: exit code |
81
82
  | 4 | `screenshot` | absent | leaf script: exit code |
82
83
 
83
- `deep` runs the four above and not the five leaf scripts, which the default
84
+ `deep` runs the four above and not the six leaf scripts, which the default
84
85
  depth already covers. The two sets are disjoint, so running both depths runs
85
86
  every declared script exactly once.
86
87
 
@@ -7,17 +7,28 @@ metadata:
7
7
 
8
8
  # Task board
9
9
 
10
- Owns the two operations that decide a task: bringing its file into existence, and declining one decided against. `task-complete` settles a task once its work exists, marking outcomes `[x]` and archiving a shipped one. Do not mark outcomes or archive here, and do not move a plan by hand.
10
+ Owns the two operations that decide a task: bringing its file into existence, and declining one decided against. `task-complete` settles a task once its work exists, so do not mark outcomes, archive, or move a plan by hand here.
11
11
 
12
12
  Read `${CLAUDE_SKILL_DIR}/references/tasks.md` before writing any file. It holds the filename convention, the frontmatter contract, and the file format. Do not work them from memory.
13
13
 
14
14
  ## Guards
15
15
 
16
- - Resolve the board at the main worktree root, not `pwd`. Run `git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd` outside a git repo. Every read and write below resolves against that root. The board is gitignored scratch shared across worktrees, so a linked worktree writing to its own `pwd` creates a second board nothing else reads.
17
- - A new task file is a main-root write, so it goes out as a heredoc. Declining already runs through `canon tasks decline`, which resolves the root in-process. Resolve that root and route the write the way `session-worktree` states.
16
+ - Resolve the board at the main worktree root with `git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd` outside a git repo
17
+ - The board is gitignored scratch shared across worktrees, so a linked worktree writing its own `pwd` creates a second board nothing reads
18
+ - A new task file is a main-root write, so it goes out as a heredoc, routed the way `session-worktree` states. Declining runs through `canon tasks decline`, which resolves the root itself.
18
19
  - If `.canon/tasks/` does not exist at that root, stop: `❌ No .canon/tasks/ board. Run canon claude init to set it up.`
19
- - Route on the request rather than on a flag. Creating names work that does not exist yet, and declining names a task file decided against. A request to archive a shipped task or mark its outcomes belongs to `task-complete`, so name it and stop. If the request fits neither, stop: `❌ Ambiguous. Say whether to create a task or decline one.`
20
- - Never hand-edit `.canon/tasks/index.md`. A hook regenerates it from sibling frontmatter after a write. Do not run the regen command directly, except after a shell write from a linked worktree: the hook matches `Write|Edit|MultiEdit` and nothing fires on `Bash`, so that one case regenerates explicitly with `canon indexes regen --no-stage --root <main-root> <main-root>/.canon/tasks/index.md`.
20
+ - Never hand-edit `.canon/tasks/index.md`, which a hook regenerates from sibling frontmatter after a write
21
+
22
+ ### Routing the request
23
+
24
+ Route on the request rather than on a flag. Creating names work that does not exist yet, and declining names a task decided against.
25
+
26
+ - Archiving or marking outcomes belongs to `task-complete`, so name it and stop
27
+ - If the request fits neither, stop: `❌ Ambiguous. Say whether to create a task or decline one.`
28
+
29
+ ### Regenerating the index by hand
30
+
31
+ Do not run the regen command directly, except after a shell write from a linked worktree. The hook matches `Write|Edit|MultiEdit` and nothing fires on `Bash`, so that one case runs `canon indexes regen --no-stage --root <main-root> <main-root>/.canon/tasks/index.md`.
21
32
 
22
33
  ## Create
23
34
 
@@ -25,99 +36,96 @@ Read `${CLAUDE_SKILL_DIR}/references/tasks.md` before writing any file. It holds
25
36
 
26
37
  Every task traces to a plan, a groundwork folder, an intake folder, or a GitHub issue. Ask for it when the request does not carry one, and stop rather than guessing: `❌ No origin. Name the plan, groundwork folder, intake folder, or issue this task comes from.`
27
38
 
28
- A task with no origin is either lost context or work nobody decided to do. This is the only moment the invariant is enforceable, because it is the only moment a task file comes into existence.
29
-
30
- Accept work whose origin is the conversation itself only when the user says so explicitly, and record what it was in the intro paragraph instead of writing a link line to a file that does not exist.
39
+ A task with no origin is lost context or work nobody decided to do, and creation is the only moment the invariant is enforceable. Accept an origin in the conversation itself only when the user says so, recording it in the intro paragraph rather than a link line.
31
40
 
32
41
  ### Stop a worker before it claims a label
33
42
 
34
- A session that asserted `canon:role-worker` this run, or whose own name in `canon sessions list --self --json` starts with `worker-`, stops here. It claims no label and writes no file. Report the row's title, its origin, and the surface it would take to the controlling session instead, since a task file with no row is a dropped task and placing the row is that session's call.
43
+ A session that asserted `canon:role-worker` this run, or whose own name in `canon sessions list --self --json` starts with `worker-`, stops here. It claims no label and writes no file.
35
44
 
36
- The guard sits ahead of Step 2 because `--claim` reserves a label on disk, so a stop after it leaves a reservation nothing uses. It names the worker role alone. A planner closing a groundwork track still writes its task file here and reaches Step 4's roster check as before.
45
+ - Report the row's title, its origin, and the surface it would take to the controlling session instead, since placing the row is that session's call
46
+ - The guard sits ahead of Step 2 because `--claim` reserves a label on disk, so a later stop leaves a reservation nothing uses
47
+ - It names the worker role alone. A planner closing a groundwork track still writes its task file here and reaches Step 4's roster check.
37
48
 
38
49
  ### Step 2: allocate the phase label
39
50
 
40
- Run `canon tasks next-label --claim --json` and take its `label` field. The verb reads the live board and its `archive/` sibling together, so the label it returns accounts for what the board alone no longer shows, and `--claim` reserves it under `.canon/ordinal-locks/` so a second session filing at the same moment gets the next one. Branch on the record's `reason` rather than the exit: `label-contended` means re-run the claim, and a usage error from an unknown `--claim` means the installed binary predates the flag, so report the verb as unavailable rather than falling back to a bare read, which reopens the race.
51
+ Run `canon tasks next-label --claim --json` and take its `label` field. The verb reads the live board and its `archive/` sibling together, and `--claim` reserves the label under `.canon/ordinal-locks/` so a concurrent session gets the next one.
41
52
 
42
- Do not derive the label from a version file. `${CLAUDE_SKILL_DIR}/../git-commit/references/versioning.md` permits free renumbering, so the verb's two folders are the only surface that knows what a label currently means.
53
+ - Branch on the record's `reason` rather than the exit. `label-contended` means re-run the claim.
54
+ - A usage error from an unknown `--claim` means the binary predates the flag. Report the verb unavailable rather than falling back to a bare read, which reopens the race.
55
+ - Do not derive the label from a version file. `${CLAUDE_SKILL_DIR}/../git-commit/references/versioning.md` permits free renumbering, so only the verb's two folders know what a label means.
43
56
 
44
57
  ### Step 3: write the file
45
58
 
46
- Write `.canon/tasks/vXX.Y-<slug>.md` under the claimed label, reading the file first if a session ever stubbed it, following the format in `${CLAUDE_SKILL_DIR}/references/tasks.md`. Include a link line only when the file or folder it names exists. A link to a plan nobody has written yet is the broken pointer the archive rules exist to prevent.
59
+ Write `.canon/tasks/vXX.Y-<slug>.md` under the claimed label, reading the file first if a session ever stubbed it, following the format in `${CLAUDE_SKILL_DIR}/references/tasks.md`. Write it immediately, since Claude Code's tool permission dialog is the confirmation gate.
47
60
 
48
- Write `Plan:`, `Groundwork:`, and `Intake:` as markdown links relative to `.canon/tasks/`, as in `Plan: [feature-<slug>](../plans/feature-<slug>.md)`. Leave `Issue:` a bare `#NNN`. A task written in the older bare-path form still parses, so it costs the board a clickable line rather than an archive, but it leaves the board in two shapes for every reader after.
61
+ - Include a link line only when the file or folder it names exists, since a link to an unwritten plan is the broken pointer the archive rules prevent
62
+ - Write `Plan:`, `Groundwork:`, and `Intake:` as markdown links relative to `.canon/tasks/`, as in `Plan: [feature-<slug>](../plans/feature-<slug>.md)`
63
+ - Leave `Issue:` a bare `#NNN`. The older bare-path form still parses, but it leaves the board in two shapes for every later reader.
64
+ - Never write a `Pull request:` line here. `git-pr` adds it when a pull request opens, and a number guessed now points at someone else's work.
49
65
 
50
- Never write a `Pull request:` line here. `git-pr` adds it when a pull request opens, and a number guessed at create time points at someone else's work.
66
+ ### Step 4: place it on a surface
51
67
 
52
- Write it immediately. Claude Code's tool permission dialog is the confirmation gate. Do not pause for approval.
68
+ A task file with no row is a dropped task, so name its surface in the same pass that creates it. The test and both file shapes are in `${CLAUDE_SKILL_DIR}/references/board.md`.
53
69
 
54
- ### Step 4: place it on a surface
70
+ | Task | Surface |
71
+ | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
72
+ | Plausibly planned within the next few waves | A row under `## Needs a plan` in `.canon/tasks/priority.md`, positioned against the rows there, with the reason in `Waiting on` |
73
+ | Anything else | A line in `.canon/tasks/backlog.md`, which is unordered |
55
74
 
56
- A task file with no row is a dropped task, so name the surface it lands on in the same pass that creates it. A task that would plausibly be planned within the next few waves takes a row under `## Needs a plan` in `.canon/tasks/priority.md`, positioned by where it sits against the rows already there, with the reason for that position in its `Waiting on` cell. Anything else takes a line in `.canon/tasks/backlog.md`, which is unordered and where in the file it goes means nothing.
75
+ ### Check for a live orchestrator
57
76
 
58
- The test and both file shapes are in `${CLAUDE_SKILL_DIR}/references/board.md`.
77
+ Check the roster before writing either file. Read `canon sessions list --self --json` for this session's own `sessionId` and `name`, then match a row in `canon sessions list --json` against three tests.
59
78
 
60
- Check the roster for a live orchestrator before writing either file. Read `canon sessions list --self --json` for this session's own `sessionId` and `name`, then read `canon sessions list --json` and match a row whose `repository` matches this session's own, whose `sessionId` differs from it, and whose `name` starts with `orchestrator-`. That is the convention every hand-launched controller on this machine currently uses, and it excludes every `worker-` and `planner-` session cleanly. Treat a refusal from either call the same as a roster read that failed.
79
+ - Its `repository` matches this session's own, and its `sessionId` differs
80
+ - Its `name` starts with `orchestrator-`, the convention every hand-launched controller here uses, which excludes every `worker-` and `planner-` session
81
+ - Treat a refusal from either call the same as a roster read that failed
61
82
 
62
- - **Found.** Do not write `priority.md` or `backlog.md`. Report the surface the row would take and the reason worked out above, then hand off by messaging that session with the same information so it places the row itself rather than two sessions writing the board at once.
63
- - **Not found, and this session's own name (already read above) starts with `worker-` or `planner-`.** Do not write either file. Those two role bodies ban a board write with no exception, and an orchestrator absent from the roster is not consent to break it. Report the row and the surface it would take, so the operator or a later orchestrator places it.
64
- - **Not found otherwise, or the roster read fails.** Write the row directly, as today. A failed read is indistinguishable from a solo project holding no orchestrator, and stopping would strand the row on the one path this section otherwise keeps unconditional. The row is a main-root write too, so it lands through the same heredoc route the file itself took.
83
+ | Roster read | Act |
84
+ | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
85
+ | **Found** | Write neither file. Report the surface and reason, and message that session so it places the row itself |
86
+ | **Not found**, and this session's name starts with `worker-` or `planner-` | Write neither file, since both role bodies ban a board write. Report the row and its surface. |
87
+ | **Not found otherwise**, or the read fails | Write the row directly, through the same heredoc route as the file. A failed read looks like a solo project. |
65
88
 
66
- Say which branch fired and why in the report. The call is a judgment restated on every sweep rather than a property of the task, and a placement with no stated reason is one the next sweep re-derives from nothing.
89
+ Say which branch fired and why in the report. A placement with no stated reason is one the next sweep re-derives from nothing.
67
90
 
68
91
  ### Step 5: report unlinked origins
69
92
 
70
93
  Scan for work that has been decided and would otherwise be forgotten. Three origins carry it, and every run reads all three.
71
94
 
72
- List `.canon/groundwork/` and run `gh issue list --state open` when a remote is configured, then grep the board for each track name and issue number. Report any with no task, one line each.
95
+ - List `.canon/groundwork/` and run `gh issue list --state open` when a remote is configured, then grep the board for each track name and issue number
96
+ - Read the dumps through `canon intake list --json`, which reports items, open, unread, and malformed per folder, and parses the contract `${CLAUDE_SKILL_DIR}/../plan-intake/references/intake.md` fixes
97
+ - Grep `.canon/tasks/`, `.canon/tasks/archive/`, and `.canon/tasks/declined/` for each folder slug, since a dump with no live task is usually promoted and settled
73
98
 
74
- Read the dumps through `canon intake list --json`, which reports items, open, unread, and malformed per folder and owns the parse of the answer contract `${CLAUDE_SKILL_DIR}/../plan-intake/references/intake.md` fixes. Then grep `.canon/tasks/`, `.canon/tasks/archive/`, and `.canon/tasks/declined/` for each folder slug. A dump with no live task is the ordinary shape of one already promoted and either shipped or declined, so a check reading the board by itself reports every settled folder as abandoned.
99
+ A dump is the stronger case for this scan. A track holds one question and stays visible, while a dump holds dozens of verdicts left with nothing carrying them forward.
75
100
 
76
- A dump is the stronger case for this scan rather than the weaker one. A track holds one question and stays visible, while a dump holds dozens of items whose verdicts were reached and then left with nothing carrying them forward.
101
+ ### The four dump states
77
102
 
78
- Those two reads give four states, and the first three earn a line each:
103
+ Those reads give four states, and the first three earn a line each.
79
104
 
80
- - Every item answered, `malformed` at zero, and none of the board, the archive, or the declined folder cites it. Decided work nobody promoted, which is what this step exists to find.
81
- - Unread items. The folder is waiting on the operator rather than forgotten, so it takes its own wording and never lands in the block above.
82
- - `malformed` above zero. An item carrying no answer slot can be reached by no verb, so name the folder as a file to repair rather than as work in either state above.
83
- - The archive cites it. Promoted and shipped, so say nothing.
105
+ | State | Report |
106
+ | ----------------------------------------------------------------------- | -------------------------------------------------------------------- |
107
+ | Every item answered, `malformed` at zero, nothing on any board cites it | Decided work nobody promoted, which is what this step exists to find |
108
+ | Unread items | Waiting on the operator rather than forgotten, in its own wording |
109
+ | `malformed` above zero | A file to repair, since no verb reaches an item with no answer slot |
110
+ | The archive cites it | Promoted and shipped, so nothing |
84
111
 
85
- `malformed` is why the first state tests two counts rather than one. A malformed item is neither unread nor answered, so reading `unread` alone folds it onto the answered side and reports a broken file as decided work nobody promoted.
112
+ The first state tests two counts because a malformed item is neither unread nor answered. Reading `unread` alone reports a broken file as decided work nobody promoted.
86
113
 
87
- Say which read fired. "No task points at this" is true of every reported state and useful about none of them.
114
+ ### How to report it
88
115
 
89
- Report rather than prompt. A track can be opened long after its task would have been written, so an offer to create one for each is noise on most runs, and that reasoning covers a dump unchanged.
116
+ Say which read fired, since "No task points at this" is true of every state and useful about none of them.
90
117
 
91
- Say the origins were read even when nothing comes back, which is the ordinary result. A step going silent on a clean pass is indistinguishable from one that never ran.
118
+ - Report rather than prompt. A track can open long after its task would have been written, so an offer per origin is noise on most runs.
119
+ - Say the origins were read even when nothing comes back, since a silent clean pass looks like one that never ran
92
120
 
93
121
  ## Decline
94
122
 
95
- Read `${CLAUDE_SKILL_DIR}/references/decline.md` when the request names a task decided against. It carries gathering the reason, the `canon tasks decline` call, the route for each refusal, and the prose to clear afterwards. Never move the file, edit `priority.md` or `backlog.md`, or regenerate the index by hand, since the command owns all three.
123
+ Read `${CLAUDE_SKILL_DIR}/references/decline.md` when the request names a task decided against. It carries gathering the reason, the `canon tasks decline` call, the route for each refusal, and the prose to clear afterwards.
96
124
 
97
- ## Output
125
+ Never move the file, edit `priority.md` or `backlog.md`, or regenerate the index by hand, since the command owns all three.
98
126
 
99
- Emit the full relative path from the project root for every file written or moved. A bare filename names a file the reader cannot open.
100
-
101
- Create:
102
-
103
- ```plaintext
104
- ✅ Created: .canon/tasks/vXX.Y-<slug>.md
105
-
106
- <label>, next after <highest>.
107
- <board or backlog, and why it landed there>.
108
-
109
- **Origin with no task:**
110
-
111
- - `.canon/groundwork/<slug>/`: open, touched <date>
112
- - `.canon/intake/<slug>/`: every item answered, nothing promoted
113
- - #NNN: <issue title>
114
-
115
- **Waiting on you:**
116
-
117
- - `.canon/intake/<slug>/`: <n> of <n> items unread
118
- - `.canon/intake/<slug>/`: <n> items carry no answer slot, so no verb reaches them
119
- ```
127
+ ## Output
120
128
 
121
- Drop either block when it carries no rows. When both are empty, which is the ordinary result, replace them with one line naming what was read: `Read <n> tracks, <n> dumps, and <n> open issues. Nothing unlinked.`
129
+ Emit the full relative path from the project root for every file written or moved, since a bare filename names a file the reader cannot open. A create run reads `${CLAUDE_SKILL_DIR}/references/create-report.md` for the report block and when each part drops. A decline run skips it.
122
130
 
123
131
  A refusal reports the reason and the resolution the mode's reference routes it to under its Step 3, on one line each.
@@ -0,0 +1,36 @@
1
+ ---
2
+ title: Create report
3
+ description: The report a task-board create run closes on, being the created path, the label and surface lines, and the two blocks Step 5's origin scan fills
4
+ ---
5
+
6
+ # Create report
7
+
8
+ Read from `## Output` of `task-board` when the run created a task. A decline run skips this file and reports through its own reference.
9
+
10
+ ## Report block
11
+
12
+ ```plaintext
13
+ ✅ Created: .canon/tasks/vXX.Y-<slug>.md
14
+
15
+ <label>, next after <highest>.
16
+ <board or backlog, and why it landed there>.
17
+
18
+ **Origin with no task:**
19
+
20
+ - `.canon/groundwork/<slug>/`: open, touched <date>
21
+ - `.canon/intake/<slug>/`: every item answered, nothing promoted
22
+ - #NNN: <issue title>
23
+
24
+ **Waiting on you:**
25
+
26
+ - `.canon/intake/<slug>/`: <n> of <n> items unread
27
+ - `.canon/intake/<slug>/`: <n> items carry no answer slot, so no verb reaches them
28
+ ```
29
+
30
+ ## When a block drops
31
+
32
+ Drop either block when it carries no rows. When both are empty, which is the ordinary result, replace them with one line naming what was read:
33
+
34
+ ```plaintext
35
+ Read <n> tracks, <n> dumps, and <n> open issues. Nothing unlinked.
36
+ ```
@@ -82,6 +82,8 @@ Two headings, `## Outcomes` and `## Findings`. Outcomes are future and checkable
82
82
 
83
83
  Add no third heading. Status stays inline on an outcome rather than becoming an "In progress" section.
84
84
 
85
+ Open each finding with a bold label naming its source and date, as in `- **planner, 2026-10-11:** <finding>`, and keep it to a few sentences. Anything longer moves into the plan or the groundwork folder. Outcomes stay a checklist, since the board tools read them by their `- [ ]` line.
86
+
85
87
  Size the outcomes so one pull request closes all of them, and split the task before handing it off rather than after. A task closes whole or not at all, so outcomes spanning two pull requests leave the board showing in-progress work no branch carries.
86
88
 
87
89
  A sliced task is the one allowed exception: a task the operator decided to ship across several pull requests on purpose, one slice each. Its `Pull request:` line lists every slice, so the board records which merged work covered which outcomes, and it closes whole when the last slice's merge finds every outcome ticked and no slice left pending.
@@ -115,7 +117,7 @@ Why this task exists and what it depends on.
115
117
 
116
118
  ## Findings
117
119
 
118
- - What constrains the task, dated where it matters.
120
+ - **<source>, <YYYY-MM-DD>:** What constrains the task.
119
121
 
120
122
  > Test strategy: <unit | component | e2e | visual | manual>, what is being verified
121
123
  ```
@@ -11,9 +11,12 @@ Owns the two operations that settle a task once its work exists: marking the out
11
11
 
12
12
  ## Guards
13
13
 
14
- - Resolve the board at the main worktree root, not `pwd`. Run `git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd` outside a git repo. The board is gitignored scratch shared across worktrees, so a linked worktree reading its own `pwd` finds no board at all.
15
- - If `.canon/tasks/` does not exist at that root, Mark has nothing to mark: report `⏭️ No .canon/tasks/ board, so no outcomes to mark.` and return, still running Step 4's receipt sweep, since `git-ship` invokes Mark on every ship and a project with no board is not a failed one. Archive stops instead: `❌ No .canon/tasks/ board. Run canon claude init to set it up.`
16
- - Route on the request rather than on a flag. A request naming a shipped task to archive, or a task file the `post-merge` hook left in place, takes Archive. Every other request takes Mark, which is what the ship chain invokes.
14
+ - Resolve the board at the main worktree root with `git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd` outside a git repo
15
+ - The board is gitignored scratch shared across worktrees, so a linked worktree reading its own `pwd` finds no board
16
+ - With no `.canon/tasks/` at that root, Mark reports `⏭️ No .canon/tasks/ board, so no outcomes to mark.` and returns, still running Step 4's sweep
17
+ - `git-ship` invokes Mark on every ship, and a project with no board is not a failed one
18
+ - With no board, Archive stops instead: `❌ No .canon/tasks/ board. Run canon claude init to set it up.`
19
+ - Route on the request rather than on a flag. A shipped task to archive, or a task file the `post-merge` hook left, takes Archive, and every other request takes Mark.
17
20
  - Never hand-edit `.canon/tasks/index.md`. A hook regenerates it.
18
21
 
19
22
  ## Mark
@@ -26,21 +29,17 @@ Resolve the base ref once:
26
29
  git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main 2>/dev/null
27
30
  ```
28
31
 
29
- Take the union of `git diff --name-only <base> HEAD`, `git diff --name-only HEAD`, and `git ls-files --others --exclude-standard`. Read content with `git diff <base> HEAD` and `git diff HEAD`.
32
+ - Take the union of `git diff --name-only <base> HEAD`, `git diff --name-only HEAD`, and `git ls-files --others --exclude-standard`
33
+ - Read content with `git diff <base> HEAD` and `git diff HEAD`
34
+ - Prefer `origin/main` over local `main`. On `main` itself the local ref resolves to HEAD, so every committed change drops out of the set.
30
35
 
31
- Prefer `origin/main` over local `main`. On `main` itself the local ref resolves to HEAD, so every committed change drops out of the set and this step goes blind to the work it is meant to read.
32
-
33
- The baseline is unusable when no merge base resolves against either ref, or when the base came from local `main` and equals HEAD. On an unusable baseline, read `${CLAUDE_SKILL_DIR}/references/unusable-baseline.md` before Step 2 for what to read instead.
36
+ The baseline is unusable when no merge base resolves against either ref, or when the base came from local `main` and equals HEAD. Then read `${CLAUDE_SKILL_DIR}/references/unusable-baseline.md` before Step 2 for what to read instead.
34
37
 
35
38
  ### Step 2: match the diff against the board
36
39
 
37
- Read `.canon/tasks/index.md` at the main worktree root, pick the task files whose title or description relates to the changed paths, and read them.
38
-
39
- Path matching only chooses which files to open. Behavior decides each outcome. For each unchecked outcome, decide whether the diff shipped the behavior that outcome names.
40
-
41
- Completion is a fact about the repository rather than a fact about the conversation, so the diff decides it and the session does not.
40
+ Read `.canon/tasks/index.md` at the main worktree root, pick the task files whose title or description relates to the changed paths, and read them. Path matching only chooses which files to open.
42
41
 
43
- Keep the match conservative:
42
+ For each unchecked outcome, decide whether the diff shipped the behavior it names, since completion is a fact about the repository and not the conversation. Keep the match conservative:
44
43
 
45
44
  - Mark only outcomes already written on the board. Never infer a new task from the diff.
46
45
  - Match on the behavior an outcome describes, not on filenames or commit subjects. The path match above only narrowed which task files to open.
@@ -49,11 +48,12 @@ Keep the match conservative:
49
48
 
50
49
  ### Step 3: close the matched outcomes
51
50
 
52
- Mark each completed outcome `[x]` in the task's own file through `canon tasks outcome <stem> --close <n> --json`, repeating `--close` for each. Positions count every outcome checkbox in file order from 1, which the read above already gives. Do not move or archive the file, even when every outcome now reads `[x]`, since the pull request has not merged and the `post-merge` hook archives the task once it does.
51
+ Mark each completed outcome `[x]` in the task's own file through `canon tasks outcome <stem> --close <n> --json`, repeating `--close` for each. Positions count every outcome checkbox in file order from 1.
53
52
 
54
- The verb resolves the board at the main worktree root in-process, which is the route `session-worktree` states for an edit inside an existing main-root file.
55
-
56
- Read `ok` and `reason` out of that record rather than the exit. An operator's shell profile may wrap `canon` in a function that runs the binary and then a second command and takes the second status, which flattens every non-zero exit to zero. A refusal arriving as success leaves the outcome unmarked while the chain moves on, so the board reports shipped work as open and the next session re-plans it.
53
+ - Do not move or archive the file, even when every outcome reads `[x]`. The pull request has not merged, and the `post-merge` hook archives the task once it does.
54
+ - The verb resolves the board at the main worktree root in-process, the route `session-worktree` states for an edit inside a main-root file
55
+ - Read `ok` and `reason` from the record rather than the exit, since a shell function wrapping `canon` can flatten a refusal to zero
56
+ - A refusal read as success leaves the outcome unmarked, so the board reports shipped work as open and the next session re-plans it
57
57
 
58
58
  ### Step 4: sweep consumed receipts
59
59
 
@@ -61,7 +61,9 @@ Read `${CLAUDE_SKILL_DIR}/references/receipt-sweep.md` for which review receipts
61
61
 
62
62
  ## Archive
63
63
 
64
- Read `${CLAUDE_SKILL_DIR}/references/archive.md` when the request names a shipped task to archive. It carries the check that the work reached `main`, the `canon tasks archive` call, the route for each refusal, and the prose to clear afterwards. Never move the file, edit `priority.md`, or regenerate the index by hand, since the command owns all three.
64
+ Read `${CLAUDE_SKILL_DIR}/references/archive.md` when the request names a shipped task to archive. It carries the check that the work reached `main`, the `canon tasks archive` call, each refusal's route, and the prose to clear.
65
+
66
+ Never move the file, edit `priority.md`, or regenerate the index by hand, since the command owns all three.
65
67
 
66
68
  ## Output
67
69