@erclx/canon 5.38.0 → 5.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/README.md +5 -5
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/SKILL.md +12 -9
  4. package/claude/skills/auto-ship/SKILL.md +43 -23
  5. package/claude/skills/backlog-triage/SKILL.md +62 -53
  6. package/claude/skills/backlog-triage/references/apply.md +69 -0
  7. package/claude/skills/build-in-slices/SKILL.md +8 -4
  8. package/claude/skills/canon-cli/SKILL.md +5 -1
  9. package/claude/skills/canon-feedback/SKILL.md +18 -8
  10. package/claude/skills/canon-feedback-triage/SKILL.md +10 -3
  11. package/claude/skills/canvas-board/SKILL.md +37 -18
  12. package/claude/skills/ci-workflow/SKILL.md +5 -4
  13. package/claude/skills/code-craft/SKILL.md +10 -6
  14. package/claude/skills/codebase-layout/SKILL.md +3 -5
  15. package/claude/skills/context-fold/REQUIREMENT.md +4 -4
  16. package/claude/skills/context-fold/SKILL.md +42 -23
  17. package/claude/skills/context-fold/references/context-refresh.md +4 -3
  18. package/claude/skills/context-fold/references/context.md +79 -64
  19. package/claude/skills/create-rule/SKILL.md +7 -3
  20. package/claude/skills/create-skill/SKILL.md +27 -7
  21. package/claude/skills/create-skill/references/skill-reference.md +41 -17
  22. package/claude/skills/decision-escalate/SKILL.md +8 -5
  23. package/claude/skills/deploy-app/SKILL.md +1 -1
  24. package/claude/skills/deprecation-migration/SKILL.md +12 -4
  25. package/claude/skills/design-taste/SKILL.md +17 -11
  26. package/claude/skills/docs-sync/REQUIREMENT.md +1 -0
  27. package/claude/skills/docs-sync/SKILL.md +17 -6
  28. package/claude/skills/docs-sync/references/docs.md +54 -21
  29. package/claude/skills/docs-sync/references/readme.md +32 -8
  30. package/claude/skills/document-health/SKILL.md +3 -4
  31. package/claude/skills/draft-and-pick/SKILL.md +73 -47
  32. package/claude/skills/draft-doc/SKILL.md +9 -4
  33. package/claude/skills/draft-doc/references/context.md +4 -4
  34. package/claude/skills/draft-doc/references/docs.md +1 -0
  35. package/claude/skills/draft-doc/references/wiki.md +3 -2
  36. package/claude/skills/draft-figure/SKILL.md +9 -7
  37. package/claude/skills/draft-identity/SKILL.md +24 -52
  38. package/claude/skills/draft-identity/references/card-route.md +46 -0
  39. package/claude/skills/draft-ready/SKILL.md +19 -7
  40. package/claude/skills/draft-screencast/SKILL.md +6 -112
  41. package/claude/skills/draft-screencast/references/beats-template.md +119 -0
  42. package/claude/skills/draft-slides/SKILL.md +9 -7
  43. package/claude/skills/draft-slides/references/deck.md +183 -0
  44. package/claude/skills/git-branch/SKILL.md +3 -3
  45. package/claude/skills/git-commit/SKILL.md +5 -3
  46. package/claude/skills/git-followup/SKILL.md +16 -9
  47. package/claude/skills/git-issue/SKILL.md +6 -5
  48. package/claude/skills/git-pr/SKILL.md +56 -44
  49. package/claude/skills/git-pr/references/evidence.md +2 -2
  50. package/claude/skills/git-pr/references/final-command.md +1 -1
  51. package/claude/skills/git-pr/references/labels.md +1 -1
  52. package/claude/skills/git-pr/references/publish.md +1 -1
  53. package/claude/skills/git-ship/SKILL.md +43 -34
  54. package/claude/skills/git-ship/references/ci-watch.md +31 -0
  55. package/claude/skills/git-split/SKILL.md +28 -100
  56. package/claude/skills/git-split/references/independent.md +40 -0
  57. package/claude/skills/git-split/references/stacked.md +73 -0
  58. package/claude/skills/git-stage/SKILL.md +9 -7
  59. package/claude/skills/git-worktree/SKILL.md +27 -49
  60. package/claude/skills/git-worktree/references/cleanup.md +55 -0
  61. package/claude/skills/markdown-craft/SKILL.md +2 -2
  62. package/claude/skills/markdown-craft/references/markdown.md +82 -53
  63. package/claude/skills/plan-feature/SKILL.md +52 -46
  64. package/claude/skills/plan-feature/references/batches.md +46 -0
  65. package/claude/skills/plan-groundwork/SKILL.md +60 -74
  66. package/claude/skills/plan-groundwork/references/output.md +48 -0
  67. package/claude/skills/plan-intake/SKILL.md +28 -21
  68. package/claude/skills/plan-intake-answer/SKILL.md +14 -13
  69. package/claude/skills/read-frames/SKILL.md +2 -0
  70. package/claude/skills/record-screencast/SKILL.md +31 -11
  71. package/claude/skills/review-address/SKILL.md +76 -158
  72. package/claude/skills/review-address/references/rebase-only.md +34 -0
  73. package/claude/skills/review-craft/SKILL.md +27 -11
  74. package/claude/skills/review-pr/SKILL.md +91 -40
  75. package/claude/skills/review-ui/SKILL.md +63 -37
  76. package/claude/skills/review-ui/references/hosted-preview.md +30 -0
  77. package/claude/skills/role-orchestrator/SKILL.md +105 -74
  78. package/claude/skills/role-orchestrator/references/orchestrator-filing.md +26 -0
  79. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
  80. package/claude/skills/role-planner/SKILL.md +66 -64
  81. package/claude/skills/role-reviewer/SKILL.md +57 -46
  82. package/claude/skills/role-worker/SKILL.md +68 -63
  83. package/claude/skills/search-craft/SKILL.md +7 -5
  84. package/claude/skills/session-compact/SKILL.md +16 -11
  85. package/claude/skills/session-map/SKILL.md +31 -10
  86. package/claude/skills/session-relay/SKILL.md +32 -65
  87. package/claude/skills/session-resume/SKILL.md +18 -13
  88. package/claude/skills/session-worktree/SKILL.md +70 -42
  89. package/claude/skills/systematic-debugging/SKILL.md +6 -1
  90. package/claude/skills/target-check/SKILL.md +13 -6
  91. package/claude/skills/target-setup/SKILL.md +39 -27
  92. package/claude/skills/task-board/SKILL.md +68 -60
  93. package/claude/skills/task-board/references/board.md +1 -1
  94. package/claude/skills/task-board/references/create-report.md +36 -0
  95. package/claude/skills/task-board/references/tasks.md +1 -1
  96. package/claude/skills/task-complete/SKILL.md +20 -18
  97. package/claude/skills/teach-workspace/SKILL.md +61 -33
  98. package/claude/skills/test-craft/SKILL.md +2 -1
  99. package/claude/skills/test-first/SKILL.md +15 -9
  100. package/claude/skills/ui-checklist/SKILL.md +18 -16
  101. package/claude/skills/ux-audit/SKILL.md +9 -10
  102. package/claude/skills/ux-measure/SKILL.md +23 -19
  103. package/claude/skills/video-craft/SKILL.md +15 -7
  104. package/claude/skills/write-human/SKILL.md +9 -1
  105. package/claude/skills/youtube-transcripts/SKILL.md +2 -1
  106. package/docs/agents/audits.md +61 -60
  107. package/docs/agents/context-audit.md +40 -58
  108. package/docs/agents/index.md +11 -61
  109. package/docs/agents/indexes.md +25 -29
  110. package/docs/agents/install-and-sync.md +125 -235
  111. package/docs/agents/markdown-audit.md +54 -96
  112. package/docs/agents/migrate.md +41 -16
  113. package/docs/agents/output-shape.md +60 -45
  114. package/docs/agents/overview.md +36 -12
  115. package/docs/agents/records.md +37 -79
  116. package/docs/agents/sessions-transfer.md +26 -70
  117. package/docs/agents/targets.md +25 -77
  118. package/docs/target/index.md +0 -1
  119. package/docs/target/migrations.md +57 -66
  120. package/docs/target/projects.md +36 -78
  121. package/docs/target/scenarios.md +9 -19
  122. package/docs/target/sync.md +27 -68
  123. package/docs/workflow/ai-workflow.md +22 -68
  124. package/docs/workflow/operating-model.md +32 -186
  125. package/docs/workflow/parallel-features.md +29 -66
  126. package/docs/workflow/skill-map-on-request.md +22 -24
  127. package/docs/workflow/skill-map.md +72 -76
  128. package/docs/workflow/visual-design-workflow.md +25 -118
  129. package/docs/workflow/zshrc-aliases.md +18 -37
  130. package/package.json +1 -1
  131. package/src/canvas/client/inspector/fields.css +4 -0
  132. package/src/canvas/index.html +6 -0
  133. package/src/claude/skills-reach.ts +2 -2
  134. package/src/cli/ui.ts +3 -1
  135. package/src/commands/canvas.ts +23 -0
  136. package/src/commands/capture.ts +14 -0
  137. package/src/commands/claude.ts +14 -1
  138. package/src/commands/context.ts +106 -7
  139. package/src/commands/demo.ts +22 -0
  140. package/src/commands/deps.ts +6 -0
  141. package/src/commands/driver.ts +28 -2
  142. package/src/commands/gov.ts +52 -18
  143. package/src/commands/hooks.ts +63 -0
  144. package/src/commands/intake.ts +17 -0
  145. package/src/commands/labels.ts +38 -5
  146. package/src/commands/markdown.ts +169 -32
  147. package/src/commands/pr.ts +160 -43
  148. package/src/commands/records.ts +8 -0
  149. package/src/commands/secrets.ts +6 -0
  150. package/src/commands/sessions.ts +8 -0
  151. package/src/commands/tasks.ts +52 -0
  152. package/src/commands/teach.ts +13 -0
  153. package/src/commands/upstream.ts +25 -1
  154. package/src/commands/worktrees.ts +5 -0
  155. package/src/context/audit.ts +274 -5
  156. package/src/context/classify/prompts.ts +1 -1
  157. package/src/context/folders.ts +1 -1
  158. package/src/context/gate.ts +23 -7
  159. package/src/context/shape-gated.ts +30 -0
  160. package/src/counts/scan.ts +1 -1
  161. package/src/design/components.ts +1 -1
  162. package/src/design/document.ts +1 -1
  163. package/src/design/tokens.ts +1 -1
  164. package/src/gate/boundaries.ts +4 -1
  165. package/src/gate/measures.ts +16 -191
  166. package/src/gate/stages.ts +14 -30
  167. package/src/git/env.ts +6 -0
  168. package/src/hooks/reply-links.ts +110 -0
  169. package/src/markdown/density.ts +258 -0
  170. package/src/markdown/gate.ts +57 -18
  171. package/src/markdown/reference-shape.ts +103 -0
  172. package/src/markdown/scope.ts +162 -0
  173. package/src/markdown/structure.ts +9 -59
  174. package/src/pr/lift.ts +153 -19
  175. package/src/pr/painted.ts +87 -0
  176. package/src/roots/record.ts +18 -1
  177. package/src/sandbox/census.ts +1 -1
  178. package/src/sandbox/headless/run.ts +3 -2
  179. package/src/sandbox/headless/watch.ts +3 -4
  180. package/src/shipped/references.ts +1 -1
  181. package/src/slides/convert/master.ts +3 -0
  182. package/src/sync/format.ts +46 -0
  183. package/src/sync/stamp.ts +6 -0
  184. package/tooling/astro/reference.md +2 -2
  185. package/tooling/base/reference.md +1 -1
  186. package/tooling/claude/seeds/.claude/hooks/reply-links.sh +72 -0
  187. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +22 -1
  188. package/tooling/claude/seeds/.claude/settings.json +4 -0
  189. package/tooling/go/reference.md +1 -1
  190. package/tooling/nextjs/reference.md +1 -1
  191. package/tooling/php/reference.md +1 -1
  192. package/tooling/python/reference.md +1 -1
  193. package/tooling/vite-react/reference.md +1 -1
  194. package/tooling/web/reference.md +1 -1
  195. package/docs/agents/canvas.md +0 -86
  196. package/docs/agents/capture.md +0 -65
  197. package/docs/agents/census.md +0 -23
  198. package/docs/agents/commands.md +0 -154
  199. package/docs/agents/comments.md +0 -34
  200. package/docs/agents/context-audit-checks.md +0 -90
  201. package/docs/agents/context-classify.md +0 -101
  202. package/docs/agents/counts.md +0 -74
  203. package/docs/agents/demo.md +0 -140
  204. package/docs/agents/docs.md +0 -27
  205. package/docs/agents/drift-surfaces.md +0 -201
  206. package/docs/agents/driver.md +0 -103
  207. package/docs/agents/gate.md +0 -90
  208. package/docs/agents/gov-detect.md +0 -47
  209. package/docs/agents/hooks.md +0 -58
  210. package/docs/agents/intake.md +0 -81
  211. package/docs/agents/key-changes.md +0 -116
  212. package/docs/agents/label-coverage.md +0 -86
  213. package/docs/agents/markdown-audit-weight.md +0 -81
  214. package/docs/agents/pr-evidence-layout.md +0 -70
  215. package/docs/agents/pr-evidence.md +0 -273
  216. package/docs/agents/pr-frames.md +0 -131
  217. package/docs/agents/pr-lift.md +0 -132
  218. package/docs/agents/pr-local.md +0 -111
  219. package/docs/agents/pr-preview.md +0 -155
  220. package/docs/agents/pr-reads.md +0 -111
  221. package/docs/agents/pr-tick.md +0 -72
  222. package/docs/agents/records-push.md +0 -61
  223. package/docs/agents/records-stale.md +0 -40
  224. package/docs/agents/records-validate.md +0 -57
  225. package/docs/agents/restated.md +0 -83
  226. package/docs/agents/routing.md +0 -61
  227. package/docs/agents/rule-citations.md +0 -90
  228. package/docs/agents/sandbox.md +0 -126
  229. package/docs/agents/scripting.md +0 -142
  230. package/docs/agents/sessions.md +0 -128
  231. package/docs/agents/skills-audit.md +0 -120
  232. package/docs/agents/skills-reach.md +0 -64
  233. package/docs/agents/slides.md +0 -176
  234. package/docs/agents/state-scoped-risk.md +0 -105
  235. package/docs/agents/superseded.md +0 -85
  236. package/docs/agents/tasks-archive.md +0 -83
  237. package/docs/agents/tasks-plans.md +0 -152
  238. package/docs/agents/tasks-validate.md +0 -133
  239. package/docs/agents/tasks.md +0 -123
  240. package/docs/agents/teach-authoring.md +0 -118
  241. package/docs/agents/teach.md +0 -157
  242. package/docs/agents/test-order.md +0 -58
  243. package/docs/agents/upstream.md +0 -94
  244. package/docs/agents/worktrees.md +0 -118
  245. package/docs/target/standards-moved.md +0 -34
  246. package/src/markdown/growth.ts +0 -43
@@ -1,83 +0,0 @@
1
- ---
2
- title: Tasks archive
3
- description: Archiving a shipped task by stem or pull request, declining one decided against, the refusal reasons each carries, and how the plan and ready folder move with the task
4
- ---
5
-
6
- # Tasks archive
7
-
8
- ## Archive
9
-
10
- `canon tasks archive` moves a shipped task from `.canon/tasks/` into `.canon/tasks/archive/`, drops its row from `priority.md`, and regenerates the board index. The three run as one unit, so the attended and unattended callers cannot archive differently.
11
-
12
- Name the task by its filename stem, or by the pull request it carries:
13
-
14
- ```bash
15
- canon tasks archive v28.1-trigger-escalation # canon-allow-reference: illustrates the stem-selection form, not a citation of a real task
16
- canon tasks archive --pull-request 673 --json
17
- ```
18
-
19
- | Option | Behavior |
20
- | -------------------- | ------------------------------------------------------------ |
21
- | `--pull-request <n>` | Select the task whose `Pull request:` line names this number |
22
- | `--json` | Emit a machine-readable record on stdout |
23
- | `--root <path>` | Board root, defaulting to the main worktree |
24
-
25
- Exit codes: `0` archived, `1` refused. Every gate is a refusal rather than a warning, because `.husky/post-merge` calls this with nobody watching. The `reason` field carries which gate fired: `no-board`, `no-match`, `ambiguous`, `no-outcomes`, `open-outcomes`, `earlier-slice`, `pending-branch`, or `bad-input`. `earlier-slice` fires when `--pull-request` names a number that is not the last on the task's `Pull request:` line, since outcomes are ticked at ship time and only the last slice's merge closes the task. `pending-branch` fires when `--pull-request` meets a `Pending branch:` line, since a branch that ticked an outcome before `git-pr` recorded its number means the last number listed is not yet the last slice. The `detail` array names each pending branch. Selecting by stem skips both checks.
26
-
27
- An outcome whose body is struck reads as cut rather than open or closed, whatever its checkbox holds: `- ~~<outcome>~~ <why>`. A task carrying only cut outcomes archives, since the gate refuses `no-outcomes` only when both the closed and the cut counts are zero. The success record carries `closed` and `cut` as counts, so a reader tells a shipped task from an abandoned one without opening the file.
28
-
29
- The task carries its plan with it. When the closing task is the last live one whose `Plan:` line resolves onto that file, the plan moves to `.canon/plans/archive/` under its own name and the archived task's link is rewritten as `[feature-<slug>](../../plans/archive/feature-<slug>.md)`, a folder deeper than the live task wrote it. A line linking several plans moves each one no other live task cites and rewrites only those links. The `plans` array on the success record carries a `from` and `to` per moved plan in line order, and is empty when nothing moved.
30
-
31
- Every other relative link in the task is re-resolved for the deeper folder too, whether its target moved or not: `Groundwork:`, `Intake:`, a `Plan:` or `Ready:` line whose target stayed put, and a link in the body. Fenced blocks, inline code spans, absolute URLs, `#` anchors, and rooted paths are left alone. `canon tasks decline` applies the same rebase, since `declined/` sits at the same depth. The plan retarget runs before the rebase and writes each moved link relative to the live board, so the rebase carries it into the deeper folder like every other link, while the ready retarget runs after it.
32
-
33
- Every other task file linking to the moved task is repointed at its new path too. The pass reads each markdown file in `.canon/tasks/`, `archive/`, and `declined/` except the moved file, `index.md`, `priority.md`, `backlog.md`, and the session handoffs, and resolves each relative link against the citing file's own folder. Only a link resolving to the old path is rewritten, with its `#` anchor kept, so an `archive/<stem>.md` link already correct, or the same stem in another folder, stays as written. Inline links and reference-style definitions (`[label]: path`) are both rewritten. Fenced blocks and inline code spans are skipped. `priority.md` and `backlog.md` are left alone because `canon tasks validate` reads a blocker there only while its target carries no `/`. Links broken before the move stay broken, and citations from plans, groundwork, and intake records are not read. The `relinked` array on the success record carries a root-relative `file` and a `links` count per file written, and is empty when nothing cited the task. The pass runs after the move, so a file it cannot read or write never stops the ordering-row removal or the index regen that follow. Each such file is left as it was and reported in `relinkFailed` as a root-relative `file` and a `message`, with a warning on stderr, and the verb still exits `0` because the move itself landed. A caller reading the record should treat a non-empty `relinkFailed` as links still pointing at the old path. `canon hooks post-merge`, which `.husky/post-merge` calls, reads the field and prints one `Links not repointed in:` line naming the files, since it discards the archive's stderr and nothing else on that unattended path would show the failure.
34
-
35
- The ready folder the task's `Ready:` line names moves with the plan to `.canon/ready/archive/`, falling back to a `.canon/ready/<nn>-<slug>/` path in the plan when the line is absent. The `Ready:` line and the plan's literal path to the folder are retargeted, and the `ready` field on the success record carries `from` and `to`, or `null` when nothing moved. A folder that resolves to nothing, sits already archived, or meets a taken destination moves nothing and refuses nothing.
36
-
37
- A plan several tasks share stays where it is, and its ready folder with it, and the task archives anyway. Moving it on the first task to close strands every sibling's pointer at a path that has gone, and `.canon/plans/` is gitignored so no history recovers the target. A `Plan:` line resolving to no file leaves the plan alone too, since a pointer somebody typed wrong is not a plan to move and holding the whole archive over it would park the board behind a repair the merge cannot make.
38
-
39
- The merge is what settles a plan. `canon hooks post-merge`, which `.husky/post-merge` calls, reads the pull request number off the squash subject and calls this verb, so both halves close in one act with nobody naming a file. That is why the move sits inside this verb rather than in a second call the hook would make after it, which could leave the task archived and the plan live.
40
-
41
- `bad-input` covers a malformed command line, which all three task verbs answer the same way. It is separate from `ambiguous` and `no-match` because those describe the board, and a caller that passed two selectors would otherwise be sent to repair a task citation that is fine.
42
-
43
- The row is matched by the link in its first cell rather than by a pattern against the whole line. A row names the task it is about in the first cell, so a link anywhere after it is a reference, such as a blocker naming what it waits on, and matching the line would drop the referring task's row too.
44
-
45
- The row removal reaches `priority.md` alone. A task gets to a merge by being planned and handed out, and both steps move it onto the board first, so one archived straight off `backlog.md` leaves its bullet standing and `canon tasks validate` reports that bullet as naming a file that is gone.
46
-
47
- The board is shared scratch at the main worktree root, so `--root` defaults to the first entry of `git worktree list` rather than the working directory. A linked worktree archives against the same board every other session reads.
48
-
49
- Skills branch on the reason rather than on the exit code:
50
-
51
- ```bash
52
- canon tasks archive --pull-request 673 --json | jq -r 'if .ok then .task else .reason end'
53
- ```
54
-
55
- ## Decline
56
-
57
- `canon tasks decline <task>` moves a task decided against from `.canon/tasks/` into `.canon/tasks/declined/`, clears whichever of `priority.md` or `backlog.md` holds its row, and regenerates the board index. Unlike `canon tasks archive`, it carries no outcome-state gate: a task can be decided against at any outcome state.
58
-
59
- ```bash
60
- canon tasks decline v28.1-trigger-escalation --reason "superseded by v30.2" # canon-allow-reference: illustrates the stem-selection form, not a citation of a real task
61
- canon tasks decline v28.1-trigger-escalation --reason "no longer needed" --by Alex --json
62
- ```
63
-
64
- | Option | Behavior |
65
- | ----------------- | ------------------------------------------------- |
66
- | `--reason <text>` | Why the task was decided against, required |
67
- | `--by <name>` | Who decided, defaulting to `git config user.name` |
68
- | `--json` | Emit a machine-readable record on stdout |
69
- | `--root <path>` | Board root, defaulting to the main worktree |
70
-
71
- Exit codes: `0` declined, `1` refused. The `reason` field carries which gate fired: `no-board`, `no-match`, `ambiguous`, or `bad-input`. `bad-input` covers a missing `--reason` and a `--by` that resolves to nobody, git config included.
72
-
73
- `DECLINE_REFUSALS` is kept apart from archive's own refusal set on purpose. Archive and decline answer different questions, shipped versus decided-against, and a shared gate would let one archive a task that cannot yet ship or decline one that already has.
74
-
75
- The decision is written onto the task as a `Declined: <reason>, <who> on <YYYY-MM-DD>` line, anchored the same way `Pull request:` is, after the last origin line the task carries.
76
-
77
- The task carries its plans with it the same way archive does, each one the declining task is the last live citation of, and the success record carries the same `plans` array. Inbound links to the declined task are repointed at `declined/<stem>.md` and reported in `relinked`, as the archive section describes. A declined task's plan lands in `.canon/plans/archive/`, indistinguishable from a shipped one by folder alone. The task file under `.canon/tasks/declined/` is what records which it was.
78
-
79
- Skills branch on the reason rather than on the exit code, the same rule `canon tasks archive` states:
80
-
81
- ```bash
82
- canon tasks decline v28.1-trigger-escalation --reason "superseded by v30.2" --json | jq -r 'if .ok then .task else .reason end' # canon-allow-reference: illustrates the stem-selection form, not a citation of a real task
83
- ```
@@ -1,152 +0,0 @@
1
- ---
2
- title: Tasks plans
3
- description: The five verbs that read or write a task's plan, being where it sits and who cites it, whether it waits on the operator, the branch it derives, what a branch reached against what it declared, and writing its Plan line
4
- ---
5
-
6
- # Tasks plans
7
-
8
- ## Plan citations
9
-
10
- `canon tasks plan-citations <stem>` answers where a task's plan sits and which other live tasks hold it. It reports and never writes.
11
-
12
- | Option | Effect |
13
- | --------------- | ------------------------------------------- |
14
- | `--json` | Emit a machine-readable record on stdout |
15
- | `--root <path>` | Board root, defaulting to the main worktree |
16
-
17
- The record carries `location`, one of `unstated`, `several`, `live`, `archived`, or `outside`, `targets`, every target the line carries in line order, and `citedBy`, the other live tasks whose `Plan:` line lands on the same file through any of its links. `several` is a line linking more than one plan, which answers no single location, so `target` is absent there and `targets` names each link. Exit codes: `0` read, `1` refused with `no-board` or `no-match`.
18
-
19
- The target resolves against the board folder and against the project root both, so `../plans/x.md` and `.canon/plans/x.md` land on the same file and one plan two tasks spelled differently counts once. Containment is tested at both record roots rather than at the one this tree resolves at, since a line somebody wrote against a root the tree has since left is still a path into the plans folder, and reading it as outside would report a shipped plan as still live. `docs/agents/records.md` states the read order.
20
-
21
- `canon tasks archive` decides its plan move on this same answer, so a caller wanting the count reads it here rather than scanning the board.
22
-
23
- Branch on `reason` rather than on the exit code, which is the rule `tasks-archive.md` already states for archive and which this verb needs for a second reason. An operator's shell profile may wrap `canon` in a function that runs the binary and then another command and takes the second status, which masks every non-zero exit rather than only an absent verb. The binary exits 1 for an unknown subcommand and 1 for an ordinary refusal alike, so the record is the only signal that survives the wrapper.
24
-
25
- A `live` location with an empty `citedBy` is the sweep to run. One whose `citedBy` names a sibling is a plan several tasks share, which the sweep leaves alone and the archive gate lets through.
26
-
27
- ```bash
28
- canon tasks plan-citations v28.1-trigger-escalation --json | jq -r '.location' # canon-allow-reference: illustrates the stem-selection form, not a citation of a real task
29
- ```
30
-
31
- ## Plan answers
32
-
33
- `canon tasks plan-answers <plan>` answers whether a plan is launchable, which is whether it still waits on the operator for a call only they can make. It reports and never writes.
34
-
35
- Name the plan by its path or by its slug, which resolve to the same file:
36
-
37
- ```bash
38
- canon tasks plan-answers dispatch-answer-gate
39
- canon tasks plan-answers .canon/plans/feature-dispatch-answer-gate.md
40
- canon tasks plan-answers ../plans/feature-dispatch-answer-gate.md
41
- ```
42
-
43
- A relative path resolves against the project root first and against `.canon/tasks/` second. The third form above is what a board row writes, since its link is relative to the board, and a dispatcher copying the reference out of the row it is dispatching has that spelling to hand rather than either of the other two. A refusal names every base it looked under.
44
-
45
- `canon tasks plan-citations` reads a task's `Plan:` line against those same two bases in the opposite order, and tests that the target lands under the live plans folder, which this verb does not. Both answer the same file for every spelling a board writes. Liveness is a separate refusal here: a plan resolving inside `.canon/plans/archive/` returns `archived` rather than a launchable reading, since it answers every question and describes work that already shipped.
46
-
47
- A plan still staging its batches with a `**Batch N**` sub-heading inside one file's `**Files to touch:**` reads `launchable: false` the same way, alongside the operator-call case. It reports as an entry in `open` labeled `Batch staging`, since the row waits on a split into one file per batch rather than on the operator. A plan stacked on a live sibling it mostly shares files with reads the same way under `Stacked sibling`, and waits on a fold or on a ``Judged apart from `feature-<parent>`: <reason>`` constraint.
48
-
49
- | Option | Effect |
50
- | --------------- | ------------------------------------------- |
51
- | `--json` | Emit a machine-readable record on stdout |
52
- | `--root <path>` | Board root, defaulting to the main worktree |
53
-
54
- The record carries `launchable` and `open`, the questions still waiting, each with the `label` that names it and the `why` its suggestion gave for needing a person. Exit codes: `0` launchable, `1` refused with `no-plan`, `archived`, or `bad-input`, `2` waiting on the operator.
55
-
56
- A blank `- Answer:` is not a waiting question. The plan standard fixes an empty slot as accepting the `- Suggested:` line above it, so the shapes this reads are `- Suggested: needs your call, <why>` and the two demonstrated paraphrases, `needs operator's call` and `needs the operator's call`, over an empty slot, which is what that standard writes where the answer turns on preference rather than on a technical default. A verb reading every blank slot as open would report every plan in the folder.
57
-
58
- The question block is read through the same parser `canon tasks validate` runs, so the gate and the conformance check cannot drift into disagreeing about what a question is. A question carrying no suggestion at all is that check's finding rather than this one's, and it goes unread here.
59
-
60
- Branch on `launchable` rather than on the exit code, for the reason the section above states: a shell profile wrapping `canon` in a function can take a later command's status and mask every non-zero exit, which reads a waiting plan as a launchable one.
61
-
62
- The orchestrator dispatch runbook calls this before it checks the branch or runs its conflict check, so a row whose plan still needs a person is handed back rather than launched into a worker that halts on the same question.
63
-
64
- ```bash
65
- canon tasks plan-answers dispatch-answer-gate --json | jq -r '.launchable'
66
- ```
67
-
68
- ## Plan branch
69
-
70
- `canon tasks plan-branch <plan>` derives the branch name from a plan file. It reports and never writes, and it names the plan the same two ways `canon tasks plan-answers` does, by path or by slug, against the same two bases.
71
-
72
- ```bash
73
- canon tasks plan-branch dispatch-answer-gate
74
- canon tasks plan-branch .canon/plans/feature-dispatch-answer-gate.md --json
75
- ```
76
-
77
- | Option | Effect |
78
- | --------------- | ------------------------------------------- |
79
- | `--json` | Emit a machine-readable record on stdout |
80
- | `--root <path>` | Board root, defaulting to the main worktree |
81
-
82
- The record carries `type`, `slug`, `branch`, `words`, and `conforms`. Exit codes: `0` derived and conforming, `1` refused with `no-plan`, `archived`, or `bad-input`, `2` derived with `conforms` false. Branch on `conforms` rather than on the exit code, which a shell function wrapping `canon` can flatten to zero.
83
-
84
- `slug` is the plan filename with its `feature-` prefix and its extension taken off, and `type` is the constant `feat`. Reading a type off the plan's prose was the alternative, and it is the half of the derivation that has already disagreed with itself: one dispatch checked `fix/path-form-hook` against a worker that took `feat/path-form-hook`, both sides reading one plan. What makes the constant safe is that a branch type is cosmetic. `git-stage` reads a commit's type off the staged diff and `git-pr` reads a title off the diff, so the semantics a release reads never pass through the branch name. What it costs is a worktree listing where every plan-derived branch reads `feat/`, and nothing renames it later.
85
-
86
- `conforms` reads both caps `claude/skills/git-branch/references/branch.md` states, being 4 words on the description and 50 characters on the whole branch. A false reading is a row for a person rather than a name to shorten here, since a rename parts the branch slug from the plan slug that `session-worktree` tier 1 and `git-pr`'s plan lookup both read back.
87
-
88
- Both sides of a dispatch call it. The orchestrator's collision check derives its candidate here, and `auto-ship` Step 0 derives the worktree it enters from the same plan, so the branch a gate clears and the branch a session takes are one string by construction. They were two readings of one paragraph until 2026-09-06, when four dispatches on one plan produced three different strings.
89
-
90
- ```bash
91
- canon tasks plan-branch dispatch-answer-gate --json | jq -r '.branch'
92
- ```
93
-
94
- ## Plan reach
95
-
96
- `canon tasks plan-reach <plan>` reads a branch back against what was written down about it. It reports and never writes, and it names the plan the same two ways `canon tasks plan-branch` does.
97
-
98
- ```bash
99
- canon tasks plan-reach dispatch-answer-gate
100
- canon tasks plan-reach dispatch-answer-gate --base origin/main --json
101
- ```
102
-
103
- | Option | Effect |
104
- | --------------- | ---------------------------------------------- |
105
- | `--base <ref>` | Far side of the range, defaulting to the trunk |
106
- | `--json` | Emit a machine-readable record on stdout |
107
- | `--root <path>` | Board root, defaulting to the main worktree |
108
-
109
- The record carries `claimed`, `undeclared`, `declared`, `base`, `changed`, `plans`, `rows`, and `board`. Exit codes: `0` read with nothing claimed, `1` refused with `no-plan`, `archived`, `bad-input`, `no-base`, or `no-diff`, `2` read with a claim standing. Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero.
110
-
111
- `claimed` leads because it is the short list and the one worth acting on. It carries one entry per path, each holding a `holders` list, so a track carrying both a live plan and a `## Run now` row reads as one holder rather than two. A holder names itself, its `source` of `plan` or `row`, and the `declaration` it matched on, so a folder claim reports which folder rather than leaving the reader to find it. A plan holder also carries `rowed`, whether that plan has a row in `## Run now` at all: a plan with no row is the shape a plan nobody archived takes, and equally the shape of one whose task has yet to be dispatched, so it narrows the reader's search rather than answering it. `undeclared` is every changed path this plan never named, which runs long on an ordinary branch: over the wave this verb was filed against, it ran 22 of 26 paths on one pull request and 18 of 25 on another. Those are the ship chain's own writes rather than scope creep, since the sync skills reach a context entry and the public docs, and the check stages regenerate what they assert.
112
-
113
- A declaration is a backticked span standing as an entry's subject, ahead of the colon opening its reason. Reading every span was the alternative and it reports pairs that were never going to collide, since a reason routinely cites a file the entry is not about: measured over the same wave, the subject rule reports 6 crossing pairs against 14 for every span. Both sides of a rename declare, since both sit ahead of the colon.
114
-
115
- The range is read at the current directory and the plans and board at the board root, so a linked worktree reads its own branch against the shared records. Reading both at one root was the alternative and it measures a main checkout sitting on the trunk, where the range closes on itself and every branch reports a reach of nothing.
116
-
117
- It reads only what is written down, so it inherits the dispatch runbook's blindness: a hand-launched track carries no row and a track with no plan carries no declaration. The `plans`, `rows`, and `board` fields say how much there was to compare against, so a clear reading over an empty corpus does not read as a proof. A missing board reports `board: false` and zero rows rather than refusing, since a project with plans and no board still has a branch worth reading.
118
-
119
- A claim is only as current as the folder it was read from, and the live folder holds a plan whose work already shipped until something archives it. `canon tasks archive` moves a plan on merge, so a plan stranded by a run that never reached the archive keeps claiming its files against every branch afterwards. Check whether the holder is actually in flight before treating a claim as a collision: the first run of this verb on its own branch reported five paths held by a plan whose verb had already merged, and the whole reading came of a file nobody archived. `canon tasks validate` is what reports the stranded plan itself.
120
-
121
- `canon:git-ship` runs it at step 5, after `git add -A` and before the commit grouping. That is the first point the branch is whole and the last before a pull request exists to carry the answer.
122
-
123
- ```bash
124
- canon tasks plan-reach dispatch-answer-gate --json | jq -r '.claimed[] | "\(.path) held by \([.holders[].name] | join(", "))"'
125
- ```
126
-
127
- ## Plan link
128
-
129
- `canon tasks plan-link <task> <plan>` writes or corrects a task's `Plan:` line, as `Plan: [<label>](<target>)` right after the H1. `plan-feature` calls it right after a plan file lands, when Step 1 resolved an existing task for the feature, so the line is a mechanical write rather than hand-edited markdown.
130
-
131
- Name the task by its filename stem, and the plan by its path or its slug, the same two forms `canon tasks plan-answers` accepts:
132
-
133
- ```bash
134
- canon tasks plan-link v28.1-trigger-escalation dispatch-answer-gate # canon-allow-reference: illustrates the stem-selection form, not a citation of a real task
135
- canon tasks plan-link v28.1-trigger-escalation .canon/plans/feature-dispatch-answer-gate.md --json
136
- ```
137
-
138
- | Option | Behavior |
139
- | --------------- | --------------------------------------------------------- |
140
- | `--add` | Append the plan to the line instead of replacing the link |
141
- | `--json` | Emit a machine-readable record on stdout |
142
- | `--root <path>` | Board root, defaulting to the main worktree |
143
-
144
- The plan resolves the same way `canon tasks plan-answers` resolves one, against the project root first and `.canon/tasks/` second, so a bare slug and a board-relative path both work. A reference resolving to no file refuses as `no-plan`, naming every base it looked under.
145
-
146
- The write adds the line when it is absent and corrects the target in place when it exists, anchored on the H1 rather than on the last origin line, since `Plan:` is the first origin line a task carries rather than the last. A `Plan:` line inside a fenced sample is skipped. The `action` field reports `added`, `appended`, `corrected`, or `unchanged`, which makes a rerun against the same plan safe, and `replaced` names the target the line carried before when it differed.
147
-
148
- A line already linking several plans is refused as `several-plans` and left unchanged, with `detail` listing each link, even when one of them is the plan named. The verb cannot tell which link to drop, and reporting `unchanged` would hide a line that breaks the one-plan rule.
149
-
150
- A sliced task ships one plan per slice, so `--add` appends the plan to the line rather than replacing the link. It keeps every link in its order, writes a bare-path target as a link so it stays readable, leaves trailing prose and the line ending where they were, adds the line when the task carries none, reports `unchanged` and writes nothing when the plan is already listed, and is allowed on a line already holding several. Without the flag the replace behavior and the `several-plans` refusal above hold.
151
-
152
- Exit codes: `0` recorded, `1` refused. The `reason` field carries `no-board`, `no-match`, `no-plan`, `several-plans`, or `bad-input`.
@@ -1,133 +0,0 @@
1
- ---
2
- title: Tasks validate
3
- description: Validating the board against the tree, the seven checks, the five report arrays, the exit codes, and the refusal reasons
4
- ---
5
-
6
- # Tasks validate
7
-
8
- `canon tasks validate` reports what each row of `priority.md` claims against what the tree holds. It reads and never writes, because a row is a session's claim about readiness and a validator that repaired one would be asserting the claim it exists to test.
9
-
10
- ```bash
11
- canon tasks validate
12
- canon tasks validate --json
13
- ```
14
-
15
- Seven checks run. Touches reaches only the `## Run now` rows, asking each to name its files, and Plan reaches every group, reading the Plan column under `## Run now` and the task file's own line under the other two. Mapping and Grouping test the folder contract and hold for every group, and Shape holds for every group too, ahead of the four. Ordering reaches only the `## Needs a plan` rows, and Blockers reaches every row outside `## Run now`:
16
-
17
- | Check | What it reports |
18
- | -------- | ----------------------------------------------------------------------------------------------------------------------- |
19
- | Shape | A row whose cell count disagrees with its table's header, or one stranded behind a table a blank line already closed |
20
- | Plan | A row whose plan citation disagrees with its group or its task file, or a task whose `Plan:` line lists one plan twice |
21
- | Mapping | A row or backlog line naming no task file |
22
- | Grouping | A task carrying a row in more than one readiness group, or on both surfaces |
23
- | Ordering | A `## Needs a plan` row whose stated position disagrees with where it actually sits, or which states no position at all |
24
- | Touches | A `## Run now` row whose Touches column names no file |
25
- | Blockers | A parked row whose blocker has stopped holding, whose cited task resolves nowhere, or whose cited task was declined |
26
-
27
- Shape runs before any other check reads a row, since a row failing it carries no dependable fields for the rest to check. A blank or prose line closes the table above it, so the walk treats the next pipe line as a fresh header candidate rather than as a continuation. That candidate counts as a header only when the line behind it is a separator carrying the same cell count, and one that fails is `row-untabled`, stranded behind a table that already closed. Cell count still has to match the header on every row that clears that test, and a row whose count disagrees is `row-misshapen`, the shape a dropped pipe or a merged column produces.
28
-
29
- The Plan check reads the row and the task file both, because the two are written by different hands and only the task's own `Plan:` line reaches the archive. A row carrying a plan whose task states none is `plan-uncited`, and a pair where the row's plan is none of the task's links is `plan-mismatched`. Both sides resolve against the board and against the project root before they compare, so a row writing `../plans/x.md` and a task writing `.canon/plans/x.md` name one file rather than two.
30
-
31
- The other two groups have no Plan column, so the same check reads the task file's own `Plan:` line against what the group name claims. A `## Needs a plan` row whose task cites a plan that sits live on disk is `plan-parked`, since the group says no such plan exists and the row is either misplaced or its plan is stale and belongs in the archive. An `## Up next` row whose task cites no live plan is `plan-absent`, since that group says one is written. A plan already archived, or a cited path with no file behind it, reads as not live on both sides. A row whose task file is gone is left to `task-unresolved`. An `## Up next` row with nothing left to build, waiting on an act only a person can take, reports `plan-absent` once its plan archives while the task stays live. That report is expected, since the check reads the task file and not the `Waiting on` prose, so a row in that state moves to `## Needs a plan` or closes.
32
-
33
- A `Plan:` line linking several distinct plans is the shape a sliced task carries, so the checks read the whole line rather than reporting it. A `## Run now` row passes when its plan is any one of the links, and a parked row is judged on whether any link is live: `## Up next` needs one, and `## Needs a plan` reports `plan-parked` when one is. The check adds no sliced marker, so an unsliced task given a second link by hand reads as sliced. `plan-several` now means a line listing one plan twice, compared by the file each link resolves to, so `../plans/x.md` beside `.canon/plans/x.md` counts. It is reported in every group and replaces the `plan-uncited` or `plan-absent` report the line would otherwise draw. The message lists the links, and the fix is to drop the repeat.
34
-
35
- Mapping spans two surfaces, because a task sits on `priority.md` when it would plausibly be planned soon and on `backlog.md` otherwise. A row or a backlog line naming no task file is `task-unresolved`, and a file both surfaces name is `row-duplicated` for the reason a task in two groups is: it claims two things about itself and only one can hold. A task file neither surface names is `unplaced` rather than a finding, since that is the normal state between a session filing it and a live orchestrator placing it on the board. One check across both is what lets a task move between them without the move reading as a dropped file.
36
-
37
- A backlog line is a bullet carrying a link to a sibling task, since the backlog is a flat unordered list rather than a table. A bullet holding prose is skipped rather than reported, which keeps the file's own intro out of the findings, and the task that bullet meant to name still lands in the `unplaced` array below rather than the findings. A project carrying no `backlog.md` reads as an empty backlog rather than a refusal.
38
-
39
- Ordering reads a `## Needs a plan` row's `Waiting on` cell for the position it claims, and reports two failures off one walk. A row stating an ordinal is checked against where it actually sits, which is `row-misordered`. A row claiming no position in either form it may take is `row-unranked`, since its cell argues the task matters and ranks it against nothing, which leaves the order recording when each row was filed.
40
-
41
- The ordinal phrase is prose rather than data, searched for anywhere in the cell rather than at its start, since every live row states its position at the end of a sentence rather than at the front. The vocabulary stops at `first` through `twentieth` plus `last`, since a parser strict enough to catch a gap would otherwise flag a row phrased correctly and differently, and bounding it to those words is what keeps a cell reading `Untestable from here` from matching on `from`. One comparison against the real position catches a gap, a duplicate, and a sequence starting somewhere other than first alike.
42
-
43
- The comparative phrase is bounded the same way and for the same reason. A closed verb list of `leads`, `heads`, `opens`, `closes`, `trails`, `precedes`, `follows`, `outranks`, and `sits under`, `above`, or `below` has to sit in one clause with a positional object, which is a `vNN.N` phase label, the word `group`, or `row` or `rows`. Both halves are needed, since a cell reading `it closes a gap the reference gate leaves open` carries the verb and claims no position, and the clause bound is what stops a verb in one half of the cell pairing with an object in the other. An ordinal exempts the row, being a comparative claim already. An unusual comparative phrasing reports as unranked, the safe direction for a check over prose, and a vocabulary verb reaching a positional object non-positionally reports as ranked, which only grading prose could separate.
44
-
45
- The Touches check is the one a person cannot run by eye. Paths come from the backticked spans in the Touches column, a span naming no file is dropped, and a directory shares any file beneath it. A `## Run now` row whose column parses to nothing is `touches-unstated`, since a row stating no file set makes a claim nothing can check.
46
-
47
- Two `## Run now` rows naming a path in common land in a second array, `shared`, which moves no exit code since the branch merging second rebases. Merge order reads it, as does a dispatcher weighing a contract, relocation, or sweep hold. Where a directory holds the other row's file, the entry names the row that claimed it, reading `both touch src/tasks, which v2.0-second claims as a folder.` The shared strings alone leave an over-broad cell and a genuine overlap identical. <!-- canon-allow-reference: illustrates the entry's own sentence shape, not a citation of a real task -->
48
-
49
- The blocker check re-takes a measurement the board records once and never repeats. Two of the five blocker kinds put a fact on disk: a dependency is settled by the cited task being archived or by its work reaching the trunk, and a collision is settled by nothing under `## Run now` still holding the file the cell cites. A cited task resolving under `.canon/tasks/declined/` instead reports separately as `blocker-declined`, since a decided-against task is neither the shipped work `blocker-settled` reports nor the dangling pointer `blocker-unresolved` reports.
50
-
51
- ```json
52
- {
53
- "findings": [
54
- {
55
- "kind": "blocker-declined",
56
- "group": "Up next",
57
- "subject": "v50.6-a-standard-no-skill-reads", // canon-allow-reference: shows the subject field's real vXX.Y-slug shape, not a citation of a real task
58
- "message": "waits on v9.0-superseded, which was declined." // canon-allow-reference: illustrates the finding's own sentence shape, not a citation of a real task
59
- }
60
- ]
61
- }
62
- ```
63
-
64
- A closed outcome is not the fact the dependency half needs. The ship chain marks outcomes as its first step and opens the pull request several steps later, so a check reading the checkbox reports the row settled while the branch is still in review. A live task therefore settles the row only once it closed every outcome and carries a `Pull request:` line the trunk holds. One that carries a `Pending branch:` line, one that names no pull request, and one whose number no trunk ref could answer for, land in the untested array below rather than being settled or left silent.
65
-
66
- The trunk is read as the clone already holds it, `origin/main` first and local `main` behind it, and no run fetches. A validate runs several times a sweep and a fetch per run is a cost this command does not carry, so a clone behind its remote under-reports rather than claiming work landed.
67
-
68
- Both halves gate on a citation inside the `Waiting on` cell, never on the columns beside it. The board format gives a collision cell the file held by the running task, so a row whose cell names no file was parked by something else, and testing its Touches column instead reports a cleared collision on a row no collision ever parked while counting that row as re-tested. A cited task is a bare sibling link, the way the Task column spells one, so a pointer into another folder names a plan rather than a task and settles nothing. A cited task carrying no outcome box settles nothing either, since a file the check could not parse is not evidence of a finished one.
69
-
70
- A citation resolving in none of the board, the archive, or the declined folder is `blocker-unresolved` rather than a settled row. Reading an absent file as archived or declined states a specific fact about a file nobody ever wrote, which is what a renamed task or a typo produces, and only a task that genuinely closed or was genuinely declined releases the row waiting on it.
71
-
72
- The other three kinds rest on a person's judgment, so a row neither half reached lands in a third array rather than in the findings:
73
-
74
- ```json
75
- {
76
- "untested": [
77
- {
78
- "group": "Needs a plan",
79
- "subject": "v50.6-a-standard-no-skill-reads", // canon-allow-reference: shows the subject field's real vXX.Y-slug shape, not a citation of a real task
80
- "message": "..."
81
- }
82
- ]
83
- }
84
- ```
85
-
86
- An untested row is not a finding and moves no exit code. Reading a clean findings list as a clean board is the failure the array exists to prevent, and `orchestrator-parked.md` is the pass that takes those rows by hand.
87
-
88
- A `## Run now` row whose Touches column names a bare folder lands in a fourth array on the same reasoning:
89
-
90
- ```json
91
- {
92
- "claims": [
93
- {
94
- "group": "Run now",
95
- "subject": "v1.0-first", // canon-allow-reference: shows the subject field's real vXX.Y-slug shape, not a citation of a real task
96
- "message": "claims the whole src/tasks folder, so it collides with every row written under it."
97
- }
98
- ]
99
- }
100
- ```
101
-
102
- That claim collides with every row a later session writes under the folder, and it is legitimate whenever the row does rewrite the directory, so the array states the reach and moves no exit code. A measure failing on a cell that is right teaches a reader to skip it. Folder against file is decided by asking the tree for a path that resolves, and by the extension only for a path the row has yet to create, since the name alone reads a file carrying no extension as a folder.
103
-
104
- The scan reaches `## Run now` and stops, where the Touches check stops. A cell in another group describes work nobody has planned, written as a sentence and rewritten once a plan exists, so a claim read off one reports on prose rather than on a file set. A parked folder claim surfaces when its row is promoted, which is when the cell becomes something a dispatcher can act on.
105
-
106
- A code span in any cell that the preview cannot break lands in a fifth array, `wide`, with the same `group`, `subject`, and `message` fields. The scan measures a span's longest whitespace-free run and flags one past 60 characters, or a brace-glob at any width. A 57-character span fit the pane and a 78-character one did not, so the limit is a heuristic and the array moves no exit code. A link target is skipped, since only its label renders, and the scan reaches every group, since the overflow it exists for sat under `## Up next`.
107
-
108
- A task file neither surface names lands in a sixth array, on the same reasoning:
109
-
110
- ```json
111
- {
112
- "unplaced": [
113
- {
114
- "subject": "v50.6-a-standard-no-skill-reads", // canon-allow-reference: shows the subject field's real vXX.Y-slug shape, not a citation of a real task
115
- "message": "is a task file with no row on the board and no line on the backlog."
116
- }
117
- ]
118
- }
119
- ```
120
-
121
- Under the roster-checked hand-off `task-board` and `plan-groundwork` state, filing a task and placing its row are two acts a different session each may take, so a task caught between the two is ordinary rather than a finding and this array moves no exit code. It still reports, since it is the only local detector for a row a hand-edit dropped, a handoff message that never arrived, or an orchestrator that ended before placing it.
122
-
123
- Exit codes: `0` every check passed, `1` refused, `2` at least one finding. The `reason` field carries which gate refused: `no-board`, `no-ordering`, or `no-groups`. A board grouping under headings of its own trips `no-groups` rather than being read against columns it never declared.
124
-
125
- Columns are read from each table's own header rather than by position, so a project whose board differs from this one is reported for what it lacks. The `index`, `priority`, and `backlog` siblings are skipped, along with every pre-compaction handoff, which takes one file per session under a `session-` prefix. None of them is a task, and a handoff counted as one would be reported as a task carrying no row on every session that wrote one.
126
-
127
- Skills branch on the findings rather than on the exit code:
128
-
129
- ```bash
130
- canon tasks validate --json | jq -r '.findings[] | "\(.kind): \(.subject)"'
131
- ```
132
-
133
- The `task-board` skill's `board.md` and `tasks.md` references carry the board format, the `Pull request:` line, and the archive rules.
@@ -1,123 +0,0 @@
1
- ---
2
- title: Tasks
3
- description: Claiming the next phase label, recording a pull request number and closing an outcome, listing each task's readiness, and where archiving, the plan verbs, and board validation are described
4
- ---
5
-
6
- # Tasks
7
-
8
- ## Next label
9
-
10
- `canon tasks next-label` reports the next unused phase label, reading `.canon/tasks/` and its `archive/` sibling together. A scan confined to the live board is blind to every label the archive already spent, which is what let two sessions hand out the same label within minutes of each other. Bare, it reports and never writes. With `--claim` it reserves the label first.
11
-
12
- ```bash
13
- canon tasks next-label
14
- canon tasks next-label --json
15
- canon tasks next-label --claim --json
16
- ```
17
-
18
- | Option | Effect |
19
- | --------------- | ------------------------------------------------ |
20
- | `--json` | Emit a machine-readable record on stdout |
21
- | `--claim` | Reserve the label so a second claim gets another |
22
- | `--root <path>` | Board root, defaulting to the main worktree |
23
-
24
- The record carries `label`, the next free `vXX.Y`, `claimed`, whether the label was reserved, and `highest`, the label it was derived from. `highest` is absent when neither folder carries a label yet, and `label` reads `v01.0` in that case, matching the zero-padded-major shape every other label already takes. <!-- canon-allow-reference: illustrates the verb's answer for a board holding no label yet, not a citation of a real task -->
25
-
26
- Exit codes: `0` derived, `1` refused with `no-board`, `2` refused with `label-contended` after five lost reservations, on `--claim` only.
27
-
28
- The minor digit rolls from 9 to 0 on the next major rather than growing a second digit, which is the single-digit-minor shape every phase label already takes. `canon tasks archive` moves a task's file from the live folder into the archive without renumbering it, so the same label counts toward the maximum wherever it currently sits, and a label claimed by two different files folds into the same scan without a dedicated check.
29
-
30
- A claim is an exclusive `mkdir` at `.canon/ordinal-locks/<label>`, and the scan counts every reservation beside the task files, since the file lands after the verb returns. Reservations never expire, so a claim that died leaves a gap, which `claude/skills/git-commit/references/versioning.md` permits. A bare read still reports without reserving, and it does not count reservations either, so it can return a label a claim already holds until that claim's task file lands. Two bare reads in the same second can also take the same answer.
31
-
32
- ```bash
33
- canon tasks next-label --json | jq -r '.label'
34
- ```
35
-
36
- ## Archive and decline
37
-
38
- `canon tasks archive` moves a shipped task into `.canon/tasks/archive/`, selected by its stem or by the pull request it carries, and `canon tasks decline` moves one decided against into `.canon/tasks/declined/`. Both carry the plan and the ready folder along when the task is their last citation, and both repoint every link to the moved task from another task file. The selectors, the gates, and the refusal reasons are in `tasks-archive.md`.
39
-
40
- ## Plan verbs
41
-
42
- Five verbs read or write the plan a task cites. `plan-citations` reports where it sits and who else cites it, `plan-answers` whether it still waits on the operator, `plan-branch` the branch it derives, `plan-reach` what a branch reached against what the plan declared, and `plan-link` writes the task's `Plan:` line. Each verb's record, exit codes, and refusals are in `tasks-plans.md`.
43
-
44
- ## Pull request
45
-
46
- `canon tasks pull-request` records the number a branch's pull request carries onto the task that branch closes. It adds `Pull request: #NNN` under the `Plan:`, `Groundwork:`, `Intake:`, or `Issue:` lines the task already holds. When the line exists it appends `, #NNN` rather than replacing it, so a task shipped in slices lists every pull request oldest first, and a number already listed leaves the line alone. A line that does not read as a comma-separated list of `#NNN` entries is replaced whole.
47
-
48
- Name the task by its filename stem, or by the plan its `Plan:` line points at:
49
-
50
- ```bash
51
- canon tasks pull-request 673 v28.1-trigger-escalation # canon-allow-reference: illustrates the stem-selection form, not a citation of a real task
52
- canon tasks pull-request 673 --plan worktree-scratch-routing --json
53
- ```
54
-
55
- | Option | Behavior |
56
- | --------------- | -------------------------------------------------- |
57
- | `--plan <slug>` | Select the task whose `Plan:` line names this plan |
58
- | `--json` | Emit a machine-readable record on stdout |
59
- | `--root <path>` | Board root, defaulting to the main worktree |
60
-
61
- A plan is matched on the token both spellings share, so `worktree-scratch-routing`, `feature-worktree-scratch-routing`, and `.canon/plans/feature-worktree-scratch-routing.md` all select the same task. The `action` field reports `added`, `appended`, `corrected`, or `unchanged`, which makes a rerun against the same number safe.
62
-
63
- Run from a branch other than `main` or `master`, the verb also drops that branch from the task's `Pending branch:` line, along with every name its reflog says it was renamed from, and removes the line once it is empty. The renamed names matter because `git-ship` renames a nonconforming branch after `task-complete` wrote the marker. It does so on an `unchanged` run too, so a rerun repairs a marker left behind. The `pending` field lists the branches still on the line after the write.
64
-
65
- Exit codes: `0` recorded, `1` refused. The `reason` field carries `no-board`, `no-match`, or `ambiguous`. `git-pr` skips silently on those three, because each is a case where a guessed write would archive the wrong task once the branch merges.
66
-
67
- A malformed argument refuses as `bad-input` instead, which sits outside that set on purpose. `git-pr` derives the number by slicing the address the create call returned, so a non-numeric value is reachable, and folding it into the swallowed set would lose the number with nothing reporting it.
68
-
69
- ## Outcome
70
-
71
- `canon tasks outcome` marks outcomes `[x]` on a task by their position in its outcome list, counting every checkbox in file order from 1.
72
-
73
- ```bash
74
- canon tasks outcome v28.1-trigger-escalation --close 1 --close 3 # canon-allow-reference: illustrates the stem-selection form, not a citation of a real task
75
- canon tasks outcome --plan worktree-scratch-routing --close 2 --json
76
- ```
77
-
78
- | Option | Behavior |
79
- | -------------------- | -------------------------------------------------- |
80
- | `--close <position>` | Outcome to mark `[x]`, 1-based, repeatable |
81
- | `--plan <slug>` | Select the task whose `Plan:` line names this plan |
82
- | `--json` | Emit a machine-readable record on stdout |
83
- | `--root <path>` | Board root, defaulting to the main worktree |
84
-
85
- An outcome already closed comes back under `alreadyClosed` rather than refusing, so a rerun against the same positions changes nothing. A position past the end of the list refuses as `out-of-range`, since a caller counting wrong should hear about it rather than mark a neighbor.
86
-
87
- A close that ticks at least one box from a branch other than `main` or `master` names that branch on a `Pending branch:` line, read from `git branch --show-current` in the working directory. The line holds the task against `canon tasks archive --pull-request` until `canon tasks pull-request` records that branch's own number. A detached HEAD, a trunk checkout, or a failed git read writes no line and refuses nothing. The line is also skipped when `gh pr list --head <branch> --state all` lists a number already on the task's `Pull request:` line, since that branch has recorded itself and a marker would outlive it, as in a review-address pass. A failed, offline, or unreadable `gh` lookup counts as no listing and writes the line. The `pending` field lists the branches on the line after the write.
88
-
89
- Positions skip fenced blocks. A checkbox inside a sample a task displays is not an outcome the task claims, and counting one would shift every position after it. `canon tasks archive` reads the list through the same walker, so the two verbs cannot disagree about which checkboxes are outcomes.
90
-
91
- Exit codes: `0` closed, `1` refused. The `reason` field adds `no-outcomes`, `out-of-range`, and `bad-input` to the three above.
92
-
93
- Both verbs exist because the write is an edit inside a file that already exists. `Edit` and `Write` refuse a main-root path from a linked worktree, and a shell stream editor is banned for in-place edits, so a verb resolving the board root in-process is the only route a skill body has. Creating a whole file needs no verb, because a heredoc through `Bash` writes it safely.
94
-
95
- ## List
96
-
97
- `canon tasks list` reports each live task file with its readiness, so a reader learns which file is parked and which is live without opening `priority.md` or `backlog.md`. It reads and never writes, and every file stays in the folder it is in.
98
-
99
- ```bash
100
- canon tasks list
101
- canon tasks list --json
102
- ```
103
-
104
- | Option | Effect |
105
- | --------------- | ------------------------------------------- |
106
- | `--json` | Emit a machine-readable record on stdout |
107
- | `--root <path>` | Board root, defaulting to the main worktree |
108
-
109
- The record carries `tasks`, one entry per file with its `stem` and its `readiness`. Readiness is one of `Run now`, `Up next`, `Needs a plan`, `backlog`, `unplaced`, or `both`. The first three are the group names the board standard fixes, `backlog` is a line on `backlog.md`, `unplaced` is a file neither surface names, and `both` is a file both surfaces name, which `validate` reports as a finding and this verb does not judge.
110
-
111
- The verb calls the parsers `validate` uses, so the two cannot disagree on what a surface names. It lists the live root only, never `archive/` or `declined/`, and skips the same siblings `validate` skips.
112
-
113
- Exit codes: `0` derived, `1` refused with `no-board`.
114
-
115
- ```bash
116
- canon tasks list --json | jq -r '.tasks[] | select(.readiness == "backlog") | .stem'
117
- ```
118
-
119
- ## Validate
120
-
121
- `canon tasks validate` reports what each row of `priority.md` and each line of `backlog.md` claims against what the tree holds. Its seven checks, the five arrays that move no exit code, and the refusals are in `tasks-validate.md`.
122
-
123
- For the board format, see `claude/skills/task-board/references/board.md`. For the `Pull request:` line and the archive rules, see `claude/skills/task-board/references/tasks.md`.