@erclx/canon 5.40.0 → 5.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +34 -17
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/references/adopted.md +34 -14
  4. package/claude/skills/auto-ship/REQUIREMENT.md +2 -0
  5. package/claude/skills/auto-ship/SKILL.md +28 -2
  6. package/claude/skills/auto-ship/references/draft-mark.md +32 -6
  7. package/claude/skills/auto-ship/references/failure-recovery.md +15 -13
  8. package/claude/skills/auto-ship/references/fan-in.md +59 -0
  9. package/claude/skills/auto-ship/references/tier-failures.md +31 -10
  10. package/claude/skills/auto-ship/references/ui-checklist.md +9 -4
  11. package/claude/skills/auto-ship/references/worktree-name.md +9 -3
  12. package/claude/skills/build-in-slices/references/adopted.md +35 -21
  13. package/claude/skills/canvas-board/references/canvas.md +17 -6
  14. package/claude/skills/canvas-board/references/design.md +26 -7
  15. package/claude/skills/code-craft/references/adopted.md +58 -20
  16. package/claude/skills/codebase-layout/references/adopted.md +49 -16
  17. package/claude/skills/codebase-layout/references/frontend.md +11 -4
  18. package/claude/skills/codebase-layout/references/tests.md +4 -2
  19. package/claude/skills/context-fold/references/classify.md +10 -15
  20. package/claude/skills/context-fold/references/completion-report.md +11 -9
  21. package/claude/skills/context-fold/references/context-refresh.md +26 -9
  22. package/claude/skills/context-fold/references/unusable-baseline.md +5 -3
  23. package/claude/skills/create-rule/REQUIREMENT.md +5 -0
  24. package/claude/skills/create-rule/SKILL.md +9 -1
  25. package/claude/skills/create-rule/references/checklist.md +26 -0
  26. package/claude/skills/create-rule/references/rule.md +98 -20
  27. package/claude/skills/create-skill/SKILL.md +1 -0
  28. package/claude/skills/create-skill/references/skill-disclosure.md +10 -2
  29. package/claude/skills/create-skill/references/skill-frontmatter.md +43 -0
  30. package/claude/skills/create-skill/references/skill-paths.md +7 -4
  31. package/claude/skills/create-skill/references/skill-practice.md +6 -3
  32. package/claude/skills/create-skill/references/skill-requirement.md +4 -2
  33. package/claude/skills/create-skill/references/skill.md +67 -44
  34. package/claude/skills/decision-escalate/REQUIREMENT.md +1 -1
  35. package/claude/skills/decision-escalate/SKILL.md +1 -2
  36. package/claude/skills/deploy-app/references/vercel.md +2 -1
  37. package/claude/skills/deprecation-migration/references/adopted.md +44 -25
  38. package/claude/skills/design-taste/references/adopted.md +9 -1
  39. package/claude/skills/design-taste/references/craft.md +3 -1
  40. package/claude/skills/design-taste/references/kinds.md +8 -7
  41. package/claude/skills/design-taste/references/preflight.md +12 -0
  42. package/claude/skills/design-taste/references/systems.md +7 -6
  43. package/claude/skills/design-taste/references/tells.md +24 -25
  44. package/claude/skills/design-taste/references/vocabulary.md +7 -5
  45. package/claude/skills/document-health/references/axes.md +54 -30
  46. package/claude/skills/draft-and-pick/references/live-arms.md +2 -2
  47. package/claude/skills/draft-doc/references/context.md +4 -3
  48. package/claude/skills/draft-doc/references/docs.md +6 -3
  49. package/claude/skills/draft-doc/references/readme/agent-facing.md +6 -0
  50. package/claude/skills/draft-doc/references/readme/application.md +8 -2
  51. package/claude/skills/draft-doc/references/readme/cli.md +5 -1
  52. package/claude/skills/draft-doc/references/readme/library.md +4 -0
  53. package/claude/skills/draft-doc/references/readme/plugin.md +9 -1
  54. package/claude/skills/draft-doc/references/readme.md +30 -13
  55. package/claude/skills/draft-doc/references/requirements.md +17 -4
  56. package/claude/skills/draft-doc/references/wiki-kind.md +20 -10
  57. package/claude/skills/draft-figure/references/architecture-set.md +7 -3
  58. package/claude/skills/draft-figure/references/figures.md +8 -6
  59. package/claude/skills/draft-figure/references/hand-drawn.md +3 -1
  60. package/claude/skills/draft-figure/references/mermaid-path.md +19 -9
  61. package/claude/skills/draft-figure/references/mermaid.md +10 -5
  62. package/claude/skills/draft-ready/references/ready.md +22 -15
  63. package/claude/skills/git-branch/REQUIREMENT.md +17 -6
  64. package/claude/skills/git-branch/SKILL.md +63 -7
  65. package/claude/skills/git-branch/references/branch.md +11 -1
  66. package/claude/skills/git-branch/references/slug.md +17 -5
  67. package/claude/skills/git-commit/references/commit.md +8 -0
  68. package/claude/skills/git-commit/references/versioning.md +23 -17
  69. package/claude/skills/git-followup/REQUIREMENT.md +1 -1
  70. package/claude/skills/git-followup/SKILL.md +5 -6
  71. package/claude/skills/git-issue/references/issue.md +29 -16
  72. package/claude/skills/git-pr/REQUIREMENT.md +1 -5
  73. package/claude/skills/git-pr/SKILL.md +11 -22
  74. package/claude/skills/git-pr/references/final-command.md +21 -8
  75. package/claude/skills/git-pr/references/labels.md +45 -28
  76. package/claude/skills/git-pr/references/pr.md +28 -9
  77. package/claude/skills/git-pr/references/publish.md +42 -28
  78. package/claude/skills/git-pr/references/task-number.md +20 -8
  79. package/claude/skills/git-ship/SKILL.md +1 -1
  80. package/claude/skills/plan-feature/references/batches.md +48 -1
  81. package/claude/skills/plan-feature/references/plan-lifecycle.md +10 -3
  82. package/claude/skills/plan-feature/references/plan.md +59 -20
  83. package/claude/skills/plan-groundwork/references/groundwork-spikes.md +4 -1
  84. package/claude/skills/plan-groundwork/references/groundwork.md +14 -16
  85. package/claude/skills/plan-intake/references/intake.md +29 -24
  86. package/claude/skills/plan-intake-answer/REQUIREMENT.md +1 -1
  87. package/claude/skills/plan-intake-answer/SKILL.md +1 -2
  88. package/claude/skills/pr-evidence/REQUIREMENT.md +40 -0
  89. package/claude/skills/pr-evidence/SKILL.md +53 -0
  90. package/claude/skills/pr-evidence/references/evidence.md +150 -0
  91. package/claude/skills/review-address/references/post-review-findings.md +10 -3
  92. package/claude/skills/review-address/references/rebase-conflicts.md +22 -7
  93. package/claude/skills/review-address/references/rest-reads.md +3 -1
  94. package/claude/skills/review-craft/references/adopted.md +35 -18
  95. package/claude/skills/review-craft/references/security.md +42 -7
  96. package/claude/skills/review-craft/references/ui.md +23 -8
  97. package/claude/skills/review-pr/SKILL.md +6 -6
  98. package/claude/skills/review-pr/references/body-variants.md +29 -9
  99. package/claude/skills/review-pr/references/close-out.md +20 -5
  100. package/claude/skills/review-pr/references/key-changes.md +27 -3
  101. package/claude/skills/review-pr/references/post.md +83 -31
  102. package/claude/skills/review-pr/references/scope.md +54 -19
  103. package/claude/skills/review-ui/SKILL.md +2 -0
  104. package/claude/skills/review-ui/references/close-out.md +49 -0
  105. package/claude/skills/review-ui/references/driving.md +24 -4
  106. package/claude/skills/review-ui/references/post.md +43 -13
  107. package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +67 -17
  108. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +81 -40
  109. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +52 -15
  110. package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +48 -26
  111. package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +12 -13
  112. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +72 -36
  113. package/claude/skills/role-orchestrator/references/orchestrator-output.md +12 -4
  114. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +64 -24
  115. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +80 -26
  116. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +41 -18
  117. package/claude/skills/role-orchestrator/references/orchestrator-resume.md +19 -7
  118. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +22 -12
  119. package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +15 -6
  120. package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +25 -7
  121. package/claude/skills/role-orchestrator/references/orchestrator-watch.md +34 -8
  122. package/claude/skills/role-orchestrator/scripts/poll.ts +16 -24
  123. package/claude/skills/role-worker/REQUIREMENT.md +2 -0
  124. package/claude/skills/role-worker/SKILL.md +17 -0
  125. package/claude/skills/search-craft/references/adopted.md +18 -6
  126. package/claude/skills/session-map/references/session.md +26 -7
  127. package/claude/skills/session-worktree/references/main-root-writes.md +25 -8
  128. package/claude/skills/systematic-debugging/references/adopted.md +30 -16
  129. package/claude/skills/target-check/references/domains.md +39 -11
  130. package/claude/skills/target-setup/references/detect.md +80 -65
  131. package/claude/skills/target-setup/references/indexes.md +8 -6
  132. package/claude/skills/task-board/references/board.md +86 -30
  133. package/claude/skills/task-board/references/decline.md +29 -13
  134. package/claude/skills/task-board/references/tasks-decline.md +12 -5
  135. package/claude/skills/task-board/references/tasks.md +65 -33
  136. package/claude/skills/task-complete/references/archive.md +41 -17
  137. package/claude/skills/task-complete/references/receipt-sweep.md +23 -9
  138. package/claude/skills/task-complete/references/unusable-baseline.md +10 -3
  139. package/claude/skills/teach-workspace/REQUIREMENT.md +1 -1
  140. package/claude/skills/teach-workspace/SKILL.md +1 -2
  141. package/claude/skills/teach-workspace/references/glossary.md +6 -2
  142. package/claude/skills/teach-workspace/references/lesson-build.md +44 -16
  143. package/claude/skills/teach-workspace/references/lesson-craft.md +12 -7
  144. package/claude/skills/teach-workspace/references/pedagogy.md +6 -3
  145. package/claude/skills/teach-workspace/references/promotion-fold.md +17 -11
  146. package/claude/skills/teach-workspace/references/promotion.md +31 -8
  147. package/claude/skills/teach-workspace/references/revisit.md +8 -2
  148. package/claude/skills/teach-workspace/references/teach.md +17 -9
  149. package/claude/skills/test-craft/references/adopted.md +48 -14
  150. package/claude/skills/test-craft/references/component.md +7 -4
  151. package/claude/skills/test-craft/references/e2e.md +25 -7
  152. package/claude/skills/test-craft/references/unit.md +3 -1
  153. package/claude/skills/test-first/references/adopted.md +12 -4
  154. package/claude/skills/ui-checklist/REQUIREMENT.md +1 -1
  155. package/claude/skills/ui-checklist/SKILL.md +1 -1
  156. package/claude/skills/video-craft/references/adopted.md +62 -18
  157. package/claude/skills/write-human/references/adopted.md +21 -8
  158. package/docs/workflow/parallel-features.md +2 -1
  159. package/docs/workflow/skill-map.md +2 -1
  160. package/package.json +1 -1
  161. package/src/claude/cases/git.ts +4 -0
  162. package/src/claude/cases/workflow.ts +5 -0
  163. package/src/commands/pr.ts +174 -37
  164. package/src/commands/records.ts +11 -3
  165. package/src/context/shape-gated.ts +1 -0
  166. package/src/markdown/scope.ts +150 -2
  167. package/src/pr/frames.ts +53 -9
  168. package/src/pr/lift.ts +6 -3
  169. package/src/pr/review-state.ts +182 -0
  170. package/src/records/validate.ts +1 -0
  171. package/src/tasks/answers.ts +79 -2
  172. package/src/tasks/fan-in.ts +176 -0
  173. package/src/tasks/stack.ts +1 -1
  174. package/tooling/astro/reference.md +80 -39
  175. package/tooling/base/reference.md +63 -32
  176. package/tooling/base/seeds/canon/context/ci.md +7 -15
  177. package/tooling/base/seeds/canon/context/development.md +14 -14
  178. package/tooling/claude/reference.md +40 -22
  179. package/tooling/cloudflare/reference.md +31 -12
  180. package/tooling/go/reference.md +47 -23
  181. package/tooling/nextjs/reference.md +52 -19
  182. package/tooling/php/reference.md +35 -16
  183. package/tooling/python/reference.md +47 -30
  184. package/tooling/shell/reference.md +6 -5
  185. package/tooling/vite-react/reference.md +49 -21
  186. package/tooling/web/reference.md +130 -70
  187. package/claude/skills/git-pr/references/evidence.md +0 -124
@@ -5,11 +5,10 @@ description: Folder layout, filename convention, file format, origin lines, and
5
5
 
6
6
  # Tasks reference
7
7
 
8
- Applies to `.canon/tasks/`. Tracks what is being built and why, at the level of features and outcomes. One file per task.
8
+ Applies to `.canon/tasks/`. Tracks what is being built and why, at the level of features and outcomes, one file per task. Update a task when it starts, completes, or changes scope.
9
9
 
10
- Update when a task starts, completes, or changes scope. When to open a task at all is project policy, not a shape rule, and lives in `CLAUDE.md`.
11
-
12
- The folder is gitignored. Board state changes when work ships rather than when a branch is written, so committing it would put a claim about the future into the diff of an unrelated pull request. The git log records what shipped.
10
+ - When to open a task at all is project policy rather than a shape rule, and lives in `CLAUDE.md`
11
+ - The folder is gitignored, since committing board state would put a claim about the future into an unrelated pull request
13
12
 
14
13
  ## Contents
15
14
 
@@ -50,17 +49,19 @@ Does not govern:
50
49
  └── v13.0-toolkit-drift.md # canon-allow-reference: illustrates the vXX.Y-slug filename this section defines
51
50
  ```
52
51
 
53
- One file per task is what keeps the board safe under parallel sessions. Two sessions working different tasks never write the same file, which matters because a gitignored board has no history to recover a clobbered write from.
54
-
55
- Siblings sit in the folder without being tasks, and each earns its place by being governed somewhere. `index.md`, `priority.md`, and `backlog.md` are governed by `board.md`. Every `session-` file is a pre-compaction handoff governed by `session.md`, and each is optional: a project whose sessions never approach a compaction carries none. Anything filtering the folder to tasks skips all of them, so a name outside the set is a task whatever it holds.
52
+ One file per task keeps the board safe under parallel sessions, since a gitignored board has no history to recover a clobbered write from.
56
53
 
57
- The `task-board` skill creates and declines task files. The `task-complete` skill marks outcomes `[x]` in an existing file and archives a shipped one, and the archive carries the task's plan with it. Neither does the other's job.
54
+ - Siblings are governed elsewhere, `index.md`, `priority.md`, and `backlog.md` by `board.md` and each `session-` file by `session.md`. A name outside that set is a task.
55
+ - `task-board` creates and declines task files, and `task-complete` marks outcomes `[x]` and archives a shipped task with its plan
58
56
 
59
57
  ## Filenames
60
58
 
61
59
  `vXX.Y-<slug>.md`, where the version is the phase label zero-padded to two digits and the slug is kebab-case.
62
60
 
63
- Padding is load-bearing. Index entries sort by filename and nothing else, so a bare `v9.0` sorts after `v15.0` and the catalog reads out of board order. `${CLAUDE_SKILL_DIR}/../git-commit/references/versioning.md` governs the label itself and permits free renumbering, so expect the occasional rename. Nothing points at a task filename, since a `Plan:` line runs from task to plan rather than the reverse. <!-- canon-allow-reference: illustrates the padding rule's own sort collision, not a citation of a real task -->
61
+ Padding is load-bearing. Index entries sort by filename alone, so a bare `v9.0` sorts after `v15.0` and the catalog reads out of board order. <!-- canon-allow-reference: illustrates the padding rule's own sort collision, not a citation of a real task -->
62
+
63
+ - `${CLAUDE_SKILL_DIR}/../git-commit/references/versioning.md` governs the label and permits free renumbering, so expect the occasional rename
64
+ - Nothing points at a task filename, since a `Plan:` line runs from task to plan rather than the reverse
64
65
 
65
66
  ## Frontmatter
66
67
 
@@ -78,15 +79,19 @@ description: Record what a target installed and report the delta against the too
78
79
 
79
80
  ## File format
80
81
 
81
- Two headings, `## Outcomes` and `## Findings`. Outcomes are future and checkable, findings are past and factual, and as flat bullets at the same indent they are visually identical. A heading separates them at no cost.
82
+ Two headings, `## Outcomes` and `## Findings`. Outcomes are future and checkable and findings are past and factual, yet as flat bullets they look identical, so a heading separates them at no cost.
82
83
 
83
- Add no third heading. Status stays inline on an outcome rather than becoming an "In progress" section.
84
+ - Add no third heading. Status stays inline on an outcome rather than becoming an "In progress" section.
85
+ - Outcomes stay a checklist, since the board tools read them by their `- [ ]` line
86
+ - Open each finding with a bold label naming its source and date, as in `- **planner, 2026-10-11:** <finding>`. Keep it to a few sentences, moving anything longer into the plan or groundwork folder.
84
87
 
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.
88
+ ### Sizing outcomes
86
89
 
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.
90
+ 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, so outcomes spanning two pull requests leave the board showing work no branch carries.
88
91
 
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.
92
+ A sliced task is the one exception, shipped across several pull requests on purpose by the operator. Its `Pull request:` line lists every slice, and it closes when the last slice's merge finds every outcome ticked and no slice pending.
93
+
94
+ ### Title and template
90
95
 
91
96
  Prefix the H1 with the `vX.Y:` phase label, then a short title whose form depends on the task type:
92
97
 
@@ -126,29 +131,49 @@ Why this task exists and what it depends on.
126
131
 
127
132
  Every task names where it came from, through a `Plan:`, `Groundwork:`, `Intake:`, or `Issue:` line under the title. Include each only when the file, folder, or issue it names exists.
128
133
 
129
- A task with no origin is either lost context or work nobody decided to do. The invariant runs both ways, and the second direction is the one that bites: a groundwork track, an intake folder, or an open issue that no task points at is work already decided and on its way to being forgotten.
134
+ ### Both directions
135
+
136
+ A task with no origin is lost context or work nobody decided to do. The reverse bites harder, since a groundwork track, intake folder, or open issue no task points at is decided work being forgotten, and an intake folder fully answered with no task citing it, live, archived, or declined, is a dump nobody acted on.
137
+
138
+ ### Link form
130
139
 
131
- An intake folder answers that direction at folder scope rather than item scope. A folder with every item answered and no task citing it, live, archived, or declined, is a dump nobody acted on.
140
+ `Plan:`, `Groundwork:`, `Intake:`, and `Ready:` name their target as a markdown link whose text is the stem, so the line resolves on a ctrl-click as `priority.md` rows do.
132
141
 
133
- `Plan:`, `Groundwork:`, and `Intake:` name their target as a markdown link whose text is the file or folder stem, so the line resolves on a ctrl-click the way `priority.md` rows already do. Write the path relative to `.canon/tasks/`, which makes it `../plans/`, `../groundwork/`, and `../intake/`. A path written from the project root renders as a link and resolves to nothing in an editor rooted at the project. `Ready:` names the ready folder a task's plan copies from, as a link to `../ready/<nn>-<slug>/` with the trailing slash a folder carries. Include it only when the folder exists. `canon tasks archive` reads it to move the folder, and falls back to a `.canon/ready/<nn>-<slug>/` path in the plan when a task carries no line.
142
+ - Write the path relative to `.canon/tasks/`, as `../plans/`, `../groundwork/`, and `../intake/`. A path from the project root resolves to nothing in an editor rooted there.
143
+ - `Issue:` stays a bare `#NNN`, since an issue number is not a path and a full URL writes the remote into a gitignored file
144
+ - `Ready:` links `../ready/<nn>-<slug>/`, the folder the plan copies from, only when it exists. `canon tasks archive` reads it to move the folder, falling back to a path in the plan.
134
145
 
135
- `Issue:` stays a bare `#NNN`, since an issue number is not a path and a full URL would write the remote into a gitignored file.
146
+ ### The `Plan:` line
136
147
 
137
- `Plan:` points at `../plans/feature-<slug>.md` while the task is open. Once the task ships and the plan is archived, it points at `../plans/archive/feature-<slug>.md`, and at `../../plans/archive/feature-<slug>.md` once the task itself is archived a folder deeper. Retarget both halves of the link rather than dropping it, so a completed task still leads to the reasoning behind it.
148
+ `Plan:` points at `../plans/feature-<slug>.md` while the task is open. Retarget both halves of the link as things move rather than dropping it, so a completed task still leads to its reasoning.
138
149
 
139
- A project that archived plans before the folder nested under `.canon/plans/` holds closed tasks pointing at `../plans-archive/` or `../.tmp/plans-archive/`. Leave those pointers where they are, since each resolves against the files it names and a task retargeted without its plan moving leads nowhere.
150
+ | State | Target |
151
+ | ----------------------------- | --------------------------------------- |
152
+ | Open | `../plans/feature-<slug>.md` |
153
+ | Shipped, plan archived | `../plans/archive/feature-<slug>.md` |
154
+ | Task archived a folder deeper | `../../plans/archive/feature-<slug>.md` |
140
155
 
141
- One plan per task, or one per slice, which the board check reads as distinct plans on one line. A plan cited by two tasks is a misfile.
156
+ - One plan per task, or one per slice as distinct plans on one line. A plan cited by two tasks is a misfile.
157
+ - `canon tasks plan-link <task> <plan>` writes or corrects the line, and `--add` appends a slice's plan, so it is never hand-edited
158
+ - Leave an older `../plans-archive/` or `../.tmp/plans-archive/` pointer alone, since it resolves and retargeting without moving the plan leads nowhere
142
159
 
143
- `canon tasks plan-link <task> <plan>` writes or corrects the `Plan:` line, and `--add` appends a slice's plan, so the line is never hand-edited.
160
+ ### The `Groundwork:` and `Intake:` lines
144
161
 
145
- `Groundwork:` points at `../groundwork/<slug>/`, the folder `plan-groundwork` fills. It names the surface it points at the way `Plan:` does. Use this key alone. `Research record` and `Decision record` are earlier spellings of the same thing and both convert to it.
162
+ `Groundwork:` points at `../groundwork/<slug>/`, the folder `plan-groundwork` fills. `Intake:` points at `../intake/<slug>/`, the folder an intake pass fills.
146
163
 
147
- `Intake:` points at `../intake/<slug>/`, the folder an intake pass fills. Use it rather than `Groundwork:`, so the line records which kind of pass produced the task. It names the folder rather than an item inside it, and the item numbers belong in that task's `## Findings`.
164
+ - Use `Groundwork:` alone for its key. `Research record` and `Decision record` are earlier spellings and both convert to it.
165
+ - Use `Intake:` rather than `Groundwork:` for an intake pass, so the line records which kind of pass produced the task
166
+ - `Intake:` names the folder rather than an item, and item numbers belong in the task's `## Findings`
148
167
 
149
- `Pull request:` records which pull requests carry the task's work, each as a bare `#NNN` the way `Issue:` does. It lists every pull request that shipped part of the task, oldest first and separated by commas, as in `Pull request: #NNN, #NNN`, and a task shipped whole lists one. It is not an origin, so a task without one is well-formed. `git-pr` appends to it when a pull request opens.
168
+ ### The `Pull request:` line
150
169
 
151
- The line is what lets a merge close its own task. `canon tasks archive --pull-request <n>` finds the task by any number the line lists and archives it only on the last. One pull request, one task: two tasks naming the same number refuse to archive rather than both moving. `Pending branch: <branch>` holds that merge back while a branch that ticked an outcome waits on its own number, since `canon tasks outcome` writes it and `canon tasks pull-request` clears it. `canon tasks outcome` skips the write when the branch's own pull request is already on the line.
170
+ `Pull request:` lists every pull request that shipped part of the task as bare numbers, oldest first and comma-separated, as in `Pull request: #NNN, #NNN`. It is not an origin, so a task without one is well-formed, and `git-pr` appends to it when a pull request opens.
171
+
172
+ The line lets a merge close its own task. `canon tasks archive --pull-request <n>` finds the task by any listed number and archives it only on the last.
173
+
174
+ - One pull request, one task. Two tasks naming the same number refuse to archive rather than both moving.
175
+ - `Pending branch: <branch>` holds that merge back while a branch that ticked an outcome waits on its own number
176
+ - `canon tasks outcome` writes that line, skipping it when the branch's pull request is already listed, and `canon tasks pull-request` clears it
152
177
 
153
178
  ## What goes in
154
179
 
@@ -161,20 +186,27 @@ The line is what lets a merge close its own task. `canon tasks archive --pull-re
161
186
 
162
187
  - Class names, file paths, function names, or prop names in any entry or title
163
188
  - Code-level steps or implementation detail. Behavioral specifics are fine.
164
- - Architectural reasoning that outlives the task. A finding explains why this task is shaped as it is. A decision the system keeps after the task closes belongs in the context entry of the domain it constrains.
189
+ - Architectural reasoning that outlives the task. A decision the system keeps after the task closes belongs in the context entry of the domain it constrains.
165
190
 
166
191
  ### Narrative and sequencing
167
192
 
168
- - Narrative of the session that produced the task. A finding states what constrains the task, so what was probed, what it cost, and who decided belongs in the groundwork folder the `Groundwork:` line names. A task with no groundwork folder cuts the narrative rather than relocating it, since the board is not the fallback destination for it.
193
+ Session history and ordering both have homes off the task file.
194
+
195
+ - Narrative of the session that produced the task. What was probed, what it cost, and who decided belongs in the groundwork folder, and a task with none cuts it, since the board is no fallback.
169
196
  - "In progress" or "Blocked" headings. Note status inline on the outcome instead.
170
- - Sequencing rationale or which version is active. Why this task is planned before its neighbors goes on its row in `priority.md`, in the cell that already carries what it is waiting on. Rationale wider than one row has no home at all, so cut it rather than filing it here.
197
+ - Sequencing rationale or which version is active. It goes on the task's `priority.md` row, and rationale wider than one row is cut.
171
198
 
172
199
  ## Archiving
173
200
 
174
- Never delete a task file. A shipped task moves to `.canon/tasks/archive/` under its own name, and the live index regenerates without it. `canon tasks archive` owns that move, and what it does and what it refuses on are at `canon tasks archive --help`.
201
+ Never delete a task file. A shipped task moves to `.canon/tasks/archive/` under its own name through `canon tasks archive`, and the live index regenerates without it. What the verb does and refuses on is at `canon tasks archive --help`, and a flat `.claude/task-archive/` names an older binary rather than a second archive.
202
+
203
+ ### The plan and ready folder
204
+
205
+ Archiving a task archives its plan alongside it when the task is that plan's last live citation. A plan several tasks share stays live with its ready folder, and the task archives anyway.
175
206
 
176
- The archive nests inside `.canon/tasks/` rather than sitting beside it as a flat `.claude/task-archive/`, which is what a binary predating this convention still writes. Meeting the flat sibling names an older checkout rather than a second archive to reconcile.
207
+ - The archived task's `Plan:` line is retargeted at `../../plans/archive/feature-<slug>.md`
208
+ - The folder its `Ready:` line names moves to `.canon/ready/archive/<nn>-<slug>/`, and both pointers to it are retargeted
177
209
 
178
- Archiving a task archives its plan alongside it, when the closing task is that plan's last live citation. The archived task's `Plan:` line is retargeted at `../../plans/archive/feature-<slug>.md`. The ready folder its `Ready:` line names moves with the plan to `.canon/ready/archive/<nn>-<slug>/`, and both pointers to it are retargeted. A plan several tasks share stays live with its ready folder, and the task archives anyway.
210
+ ### Open outcomes
179
211
 
180
- A task with an open outcome stays on the board, and so does its plan. Close it, or cut it from the task when the work is being abandoned, so what was dropped is recorded rather than inferred from an archived file. Cutting means striking the outcome's body: `- ~~<outcome>~~ <why>`. A struck body reads as cut whatever its checkbox holds, so a task carrying only cut outcomes still archives.
212
+ A task with an open outcome stays on the board, and so does its plan. Close it, or cut an abandoned outcome by striking its body, as `- ~~<outcome>~~ <why>`, which reads as cut whatever its checkbox holds, so a task of cut outcomes still archives.
@@ -7,21 +7,22 @@ description: How task-complete confirms a task's work reached main, runs canon t
7
7
 
8
8
  The Archive mode of `task-complete`. The session reads this file when the request names a task file already shipped.
9
9
 
10
- The `post-merge` git hook archives the task a merge closed, so a request arriving here is usually one the hook could not resolve on its own. Run the steps below against whatever the hook left in place.
11
-
12
- Do not move the file, edit `priority.md`, or regenerate the index by hand. `canon tasks archive` owns all three as one unit and the hook calls the same command, so a hand-rolled move here drifts from the unattended path.
10
+ - The `post-merge` git hook archives the task a merge closed, so a request here is usually one the hook could not resolve. Run the steps against whatever it left in place.
11
+ - `canon tasks archive` moves the file, edits `priority.md`, and regenerates the index as one unit. The hook calls the same command, so a hand-rolled move drifts from the unattended path.
13
12
 
14
13
  ## Step 1: confirm the work reached main
15
14
 
16
- `task-complete` marks outcomes on the branch as step 2 of the ship chain, so an all-`[x]` task routinely describes a pull request that is still open. The command gates on the outcomes and cannot tell those two apart, which is what puts this check here:
15
+ `task-complete` marks outcomes on the branch as step 2 of the ship chain, so an all-`[x]` task routinely describes a pull request still open. The command gates on the outcomes and cannot tell the two apart, which puts this check here:
17
16
 
18
17
  ```bash
19
18
  git fetch origin main --quiet && git log origin/main --oneline -20
20
19
  ```
21
20
 
22
- Match the shipped outcomes against that log, widening to `gh pr list --state merged --limit 20` when a remote is configured and the log does not settle it. When the work is not on `main`, name the task and stop: `❌ Work not on main. Archiving now loses the task if the pull request is abandoned.`
21
+ Match the shipped outcomes against that log, widening to `gh pr list --state merged --limit 20` when a remote is configured and the log does not settle it.
23
22
 
24
- The board is gitignored, so an archived task has no history behind it and nothing restores one archived early. Skip this check when the task carries a `Pull request:` line and the last number it lists is merged, since the number already proves what the log is being read for.
23
+ - When the work is not on `main`, name the task and stop: `❌ Work not on main. Archiving now loses the task if the pull request is abandoned.`
24
+ - The board is gitignored, so nothing restores a task archived early
25
+ - Skip the check when the task's `Pull request:` line lists a merged last number, which already proves what the log is read for
25
26
 
26
27
  ## Step 2: run the archive
27
28
 
@@ -31,28 +32,51 @@ Pass the task's filename stem, or the pull request number when the request names
31
32
  canon tasks archive <stem> --json
32
33
  ```
33
34
 
34
- The command refuses rather than reports, and the refusal reaches this skill through the record rather than through the exit. Branch on `ok`, then on `reason`. 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 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.
35
+ The command refuses rather than reports, so branch on the record's `ok`, then on `reason`, never on the exit.
36
+
37
+ - An operator's shell profile may wrap `canon` in a function that takes a second command's status, masking every non-zero exit
38
+ - The binary exits 1 for an unknown subcommand and an ordinary refusal alike, so the record is the only signal that survives the wrapper
39
+
40
+ ### Success record
41
+
42
+ A successful archive returns one record naming what moved.
43
+
44
+ | Field | Holds |
45
+ | -------------------- | --------------------------------------------------------------------- |
46
+ | `from`, `to` | The task's old and new path |
47
+ | `priorityRowRemoved` | Whether a `priority.md` row was dropped |
48
+ | `indexRegenerated` | Whether the index changed |
49
+ | `plans` | A `from` and `to` per live plan the task last cited, in `Plan:` order |
35
50
 
36
- On success the record carries `from`, `to`, `priorityRowRemoved`, and `indexRegenerated`, which is what moved, what row it cleared, and whether the index changed. It also carries `plans`, one `from` and `to` for each live plan the task was the last live citation of, in the order its `Plan:` line links them. An empty `plans` means the task cited no live plan, or that a sibling still holds every one it cited.
51
+ An empty `plans` means the task cited no live plan, or a sibling still holds every one it cited.
37
52
 
38
53
  ## Step 3: route on a refusal
39
54
 
40
- Each reason has one resolution and none of them is to archive around it:
55
+ Each reason has one resolution, and none of them is to archive around it.
41
56
 
42
- - `open-outcomes`: the named outcomes are unmarked or genuinely open. Run `task-complete` when the work shipped and nothing marked it. Leave the task on the board when the outcome is real. When the work is being abandoned, cut it by striking the body, `- ~~<outcome>~~ <why>`, whatever the checkbox holds, so the board records what was dropped rather than meeting this refusal a second time.
43
- - `ambiguous`: two tasks name one pull request, which is the misfile `${CLAUDE_SKILL_DIR}/references/tasks.md` rules out. Resolve the citation by hand, since no sweep repairs it.
44
- - `earlier-slice`: the number is listed on the task but is not the last, so a later slice is still to merge. Leave the task on the board. Its last slice's merge archives it.
57
+ - `open-outcomes`: the named outcomes are unmarked or genuinely open. Run `task-complete` when the work shipped unmarked, and leave the task on the board when the outcome is real.
58
+ - `ambiguous`: two tasks name one pull request, the misfile `${CLAUDE_SKILL_DIR}/references/tasks.md` rules out. Resolve the citation by hand, since no sweep repairs it.
59
+ - `earlier-slice`: the number is listed on the task but is not the last. Leave the task on the board, and its last slice's merge archives it.
60
+
61
+ ### Abandoned outcomes
62
+
63
+ When the work behind an open outcome is abandoned, cut it by striking the body, `- ~~<outcome>~~ <why>`, whatever the checkbox holds. The board then records what was dropped rather than meeting this refusal again.
45
64
 
46
65
  ### Branches, matches, and input
47
66
 
48
- - `pending-branch`: a branch named in `detail` ticked an outcome before its own pull request number was recorded. Leave the task live, since that branch's merge archives it once `git-pr` records the number. When the operator decides a named branch was abandoned, archive by stem, which is the one case for it.
67
+ The remaining three name a pending branch, a missing match, or a malformed call.
68
+
69
+ - `pending-branch`: a branch named in `detail` ticked an outcome before its pull request number was recorded. Leave the task live, since that merge archives it once `git-pr` records the number.
70
+ - An operator who decides the named branch was abandoned archives by stem, the one case for it
49
71
  - `no-match`: the stem or number names nothing on the board. Check the name against the listed stems.
50
- - `bad-input`: the command line was wrong rather than the board. Read the message, fix the arguments, and run it again. Nothing on the board needs repair, which is what separates this from the two above.
72
+ - `bad-input`: the command line was wrong rather than the board, so nothing needs repair. Read the message, fix the arguments, and rerun.
73
+
74
+ ### What to leave alone
51
75
 
52
- Do not move a plan by hand from this skill. The command carries the plan with the task when no other live task cites it, and retargets the archived task's `Plan:` line at the new path. A second mover drifts into relocating the same file differently.
76
+ Never move a plan by hand from this skill. The command carries the plan along when no other live task cites it and retargets the `Plan:` line, and a second mover relocates the same file differently.
53
77
 
54
- Leave `TASK-ARCHIVE.md` alone when it is present in the archive folder. It records the single-file era in the shape that era used, and splitting it would fabricate per-task files nobody wrote.
78
+ Leave `TASK-ARCHIVE.md` alone when it is present in the archive folder. It records the single-file era in that era's shape, and splitting it would fabricate per-task files nobody wrote.
55
79
 
56
80
  ## Step 4: clear prose naming the task
57
81
 
58
- The command drops the task's row from `.canon/tasks/priority.md` and leaves prose alone. Remove any sentence that still names the archived task or counts the rows that changed, since a stale count reads as board state.
82
+ The command drops the row from `.canon/tasks/priority.md` and leaves prose alone. Remove any sentence that still names the archived task or counts the changed rows, since a stale count reads as board state.
@@ -5,25 +5,39 @@ description: How task-complete's marking step sweeps branch review reports whose
5
5
 
6
6
  # Sweep consumed receipts
7
7
 
8
- Step 4 of `task-complete`'s Mark mode, reached in order on every marking run.
8
+ Step 4 of `task-complete`'s Mark mode, reached in order on every marking run. Sweep the review receipts a shipped branch left behind.
9
9
 
10
- Sweep the review receipts a shipped branch left behind. Resolve all paths at the main worktree root, not the current worktree, the way `session-worktree` does.
10
+ - Resolve all paths at the main worktree root, not the current worktree, the way `session-worktree` does
11
+ - Every delete is a plain `rm`, one per call, routed the way `session-worktree` states
11
12
 
12
- Every delete below is a plain `rm`, one per call, routed the way `session-worktree` states.
13
-
14
- Plans are not swept here. A plan is settled by the merge rather than by an outcome this run marked, and `canon tasks archive` moves it with the task the `post-merge` hook archives. Sweeping it from this step would read a closure Step 3 wrote moments earlier and move a plan the branch is still building from.
13
+ Plans are not swept here. A merge settles a plan, and `canon tasks archive` moves it with the task the `post-merge` hook archives, so sweeping it here would move a plan the branch is still building from.
15
14
 
16
15
  ## Reviews
17
16
 
18
17
  Leave the current branch's review receipt where it is. No skill writes one now, so one that exists is older and its branch is still live.
19
18
 
20
- The body that writes a receipt owns its lifetime. This skill sweeps on behalf of whatever called it and has no way to read whether a file is still in use, where the chain that wrote this one cites it in its own output and knows. What reaps it is the branch sweep below, one branch later, once the branch it names is gone.
19
+ The body that writes a receipt owns its lifetime. This skill cannot read whether a file is still in use, so the branch sweep below reaps it one branch later, once its branch is gone.
20
+
21
+ ### The branch sweep
22
+
23
+ Sweep the branch reports this session never opened, deleting each whose slug matches no live branch.
24
+
25
+ 1. List `.canon/review/branch-*.md`.
26
+ 2. Run the slug transform in `${CLAUDE_SKILL_DIR}/../git-branch/references/slug.md` over every name `git branch --format='%(refname:short)'` prints.
27
+ 3. Delete a report whose slug matches none of them.
28
+
29
+ Take the names from that format rather than `git branch --list`, which marks the current branch with `* ` and one checked out elsewhere with `+ `. Reading those marks as written turns a live branch into a slug nothing matches and sweeps a report a sibling worktree still uses.
30
+
31
+ ### Why sweep at all
32
+
33
+ A branch report is read once, by the session addressing it. The durable record is the comment `review-pr` posts on the pull request, so a report outliving its branch holds nothing.
21
34
 
22
- Sweep the branch reports this session never opened. List `.canon/review/branch-*.md`, run the slug transform in `${CLAUDE_SKILL_DIR}/../git-branch/references/slug.md` over every name `git branch --format='%(refname:short)'` prints, and delete a report whose slug matches none of them. Take the names from that format rather than from `git branch --list`, which marks the current branch with `* ` and a branch checked out in another worktree with `+ `, so a transform reading the marked lines as written turns a live branch into a slug nothing matches and sweeps a report a sibling worktree is still working from. A branch report is read once, by the session addressing it, and the durable record of what a review found is the comment `review-pr` posts on the pull request, so a report outliving its branch is holding nothing. Skipping this leaves them accumulating for the life of the checkout, since a slug is unique per feature and no later branch ever looks for one.
35
+ - Skipping the sweep leaves reports piling up for the life of the checkout, since a slug is unique per feature and no later branch looks for one
36
+ - The sweep also removes a local-only review on a branch deleted before it opened a pull request. It runs anyway, since nothing else clears them.
23
37
 
24
- What that removes is a local-only review on a branch deleted before it opened a pull request. The sweep runs anyway rather than keeping every report against the one case, since nothing else ever clears them.
38
+ ### What stays
25
39
 
26
- Do not sweep `ux-audit-*.md` or `ux-measure-*.md` (standalone deliverables). Those sit at `.canon/review/` itself rather than under a producer folder, so the glob above never reaches them.
40
+ Do not sweep `ux-audit-*.md` or `ux-measure-*.md`, which are standalone deliverables. They sit at `.canon/review/` itself rather than under a producer folder, so the glob never reaches them.
27
41
 
28
42
  Output one line per file swept:
29
43
 
@@ -12,8 +12,15 @@ The baseline is unusable in two cases:
12
12
  - No merge base resolves against either ref.
13
13
  - The base came from local `main` and equals HEAD. Nothing is pushed to compare against, so a narrow read reports no changes rather than admitting it cannot see them.
14
14
 
15
- An unusable baseline costs only the committed half. `git diff <base> HEAD` is empty by definition once the base equals HEAD, while `git diff HEAD` and `git ls-files --others --exclude-standard` still report uncommitted and untracked work at correct scope.
15
+ An unusable baseline costs only the committed half. `git diff <base> HEAD` is empty once the base equals HEAD, while `git diff HEAD` and `git ls-files --others --exclude-standard` still report uncommitted and untracked work at correct scope.
16
16
 
17
- Recover the committed half. Read `git log -p -1`, widening to `git log -p -<n>` when the session spans several commits, and read the candidate task files against the working tree. That yields names and content both, which is what lets Step 2 decide on behavior rather than on filenames. A fresh `git init` on `main` with no remote is the ordinary shape of a scaffolded project, so this path carries the evidence rather than covering an edge case.
17
+ ## Recover the committed half
18
18
 
19
- On a fresh `git init` project the last commit is the scaffold commit, so `git log -p -1` is the whole tree by another route. The marking step tolerates that because it only reads the diff, and it matches conservatively against outcomes already on the board. Widening what a step reads is safe. Never carry the widened set into a step that writes per file.
19
+ Read `git log -p -1`, widening to `git log -p -<n>` when the session spans several commits, and read the candidate task files against the working tree. That yields names and content both, so Step 2 decides on behavior rather than filenames.
20
+
21
+ - A fresh `git init` on `main` with no remote is the ordinary shape of a scaffolded project, so this path is common rather than an edge case
22
+ - There the last commit is the scaffold commit, so `git log -p -1` is the whole tree by another route
23
+
24
+ ## Why the widened read is safe
25
+
26
+ The marking step tolerates the whole tree because it only reads the diff and matches conservatively against outcomes already on the board. Widening what a step reads is safe, and the widened set never carries into a step that writes per file.
@@ -62,4 +62,4 @@ Two failures land specifically on where the folder sits. A workspace resolved ag
62
62
  - Creating a wiki folder a project never chose
63
63
  - What the verbs do internally, which is the CLI domain's own contract rather than this skill's, and the records backup that carries the folder off one disk
64
64
  - What a lesson renders as and how a quiz behaves once rendered, which the lesson-craft reference shapes and a rendering layer executes
65
- - Deciding when to fire. The skill is user-invoked through `disable-model-invocation`, so opening a workspace is the learner's call rather than a description match.
65
+ - Deciding when to fire. The description states `Do NOT auto-trigger`, so opening a workspace is the learner's call rather than a description match.
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: teach-workspace
3
- description: Opens and runs a learning workspace on one subject, holding a mission, resources, numbered lessons, reference pages, a glossary, and learning records that survive across sessions, proposes where a durable page from one belongs once it outgrows the workspace, and lands the confirmed pages from a branch. Use when asked to "teach me X", "open a learning workspace", "I want to learn X", "quiz me on this", "continue the lesson", "resume my workspace on X", "promote this reference page", or "fold the promoted pages in". Do NOT use to write project documentation, which belongs to the surface owning that document, and do NOT use to answer one question, which is an ordinary reply.
4
- disable-model-invocation: true
3
+ description: Opens and runs a learning workspace on one subject, holding a mission, resources, numbered lessons, reference pages, a glossary, and learning records that survive across sessions, proposes where a durable page from one belongs once it outgrows the workspace, and lands the confirmed pages from a branch. Use when asked to "teach me X", "open a learning workspace", "I want to learn X", "quiz me on this", "continue the lesson", "resume my workspace on X", "promote this reference page", or "fold the promoted pages in". Do NOT use to write project documentation, which belongs to the surface owning that document, and do NOT use to answer one question, which is an ordinary reply. Do NOT auto-trigger.
5
4
  argument-hint: <subject to learn, the topic of a workspace to resume or promote, or fold>
6
5
  metadata:
7
6
  family: answer
@@ -42,6 +42,8 @@ A glossary failing these is non-conforming even when it satisfies every shape ru
42
42
 
43
43
  ### Placement and order
44
44
 
45
+ Each entry points at its first use and sits where a reader would look for it.
46
+
45
47
  - Name where the term first appears, so a reader can reach one use of it in context.
46
48
  - Keep an entry to the meaning. Worked detail belongs on the page that teaches the term.
47
49
  - Sort entries alphabetically, so a reader who knows only the word finds it without reading the file.
@@ -59,14 +61,16 @@ A glossary failing these is non-conforming even when it satisfies every shape ru
59
61
  - Keep a short glossary as one alphabetical list under the title. Grouping a handful of entries costs a heading per category and saves no lookup.
60
62
  - Group a glossary long enough that one list stops helping under `##` headings by category, sorted alphabetically within each. Roughly two screens of entries is the signal.
61
63
  - Name each category so a reader picks it from the term alone. A category a reader cannot predict makes the grouping a second thing to search.
62
- - State a departure from any rule above in the file itself, naming what it departs from and why. A glossary serving no single body of material is the case that produces one, since a term drawn from everywhere has no first appearance to name.
64
+ - State a departure from any rule above in the file itself, naming what it departs from and why
65
+ - A glossary serving no single body of material is the case that produces one, since a term drawn from everywhere has no first appearance to name
63
66
 
64
67
  ## Rendered grouping
65
68
 
66
69
  Applies to a rendered glossary page, never to the source file above, which stays the flat alphabetical list the `## Grouping` rules above govern.
67
70
 
68
71
  - Group a rendered glossary by the lesson its own "First seen in" citation names, ordered by lesson order, under a sub-heading naming the lesson's title rather than its filename.
69
- - Trail with an "Other terms" group holding any entry the citation cannot attribute to a lesson, whether it names a reference page instead or carries no citation at all. Keep it in the alphabetical order the source file already carries.
72
+ - Trail with an "Other terms" group holding any entry the citation cannot attribute to a lesson, whether it names a reference page or carries no citation
73
+ - Keep "Other terms" in the alphabetical order the source file already carries
70
74
  - Keep the workspace-wide term filter matching against every group, and drop a group's own heading once filtering leaves nothing under it.
71
75
 
72
76
  ## Template
@@ -12,26 +12,40 @@ Mechanics rather than judgment. `lesson-craft.md` covers what a lesson should be
12
12
  `canon teach render` takes a JSON array of blocks and renders it through the same components a lesson body composes. Six types, and every structural body composes from them:
13
13
 
14
14
  - `{"type":"heading","level":1|2,"text":"<text>"}`: an `<h1>` or `<h2>`.
15
- - `{"type":"paragraph","text":"<text>","lede":true,"cites":[1]}`: a `<p>`, marked `lede` for the dek that opens the lesson. `cites` adds a footnote marker per reference number after the text, and a lede takes none.
15
+ - `{"type":"paragraph","text":"<text>","lede":true,"cites":[1]}`: a `<p>`, marked `lede` for the opening dek. `cites` adds a footnote marker per reference number, and a lede takes none.
16
16
  - `{"type":"list","items":["<text>", ...],"ordered":true}`: a `<ul>` or, with `ordered`, an `<ol>`.
17
17
 
18
18
  ### Reference, code, and raw blocks
19
19
 
20
- - `{"type":"refs","items":[{"title":"<name>","url":"<https URL>","note":"<text>"}, ...]}`: the lesson's one reference list, numbered from 1 in item order. `url` and `note` are optional, and a `url` must be http or https.
21
- - `{"type":"code","lang":"ts","text":"<source>"}`: a code sample, highlighted at render time with no script. `lang` is optional and takes a highlight.js name or alias. One the renderer does not register renders as plain escaped code.
20
+ The other three carry sources, code, and markup the components cannot.
21
+
22
+ - `{"type":"refs","items":[{"title":"<name>","url":"<https URL>","note":"<text>"}, ...]}`: the one reference list, numbered from 1. `url` and `note` are optional, and a `url` must be http or https.
23
+ - `{"type":"code","lang":"ts","text":"<source>"}`: a sample highlighted at render time with no script. `lang` is optional and takes a highlight.js name or alias, and an unregistered one renders plain.
22
24
  - `{"type":"raw","html":"<markup>"}`: the escape hatch, passed through unescaped.
23
25
 
24
- Code never travels through `raw`. A hand-written `<pre><code>` renders with no color, and `nav` never highlights markup already in a lesson body, so a sample written that way stays plain for good. A fenced block in a reference page's markdown is highlighted by `nav` on its own.
26
+ ### Where code and raw go
27
+
28
+ Code never travels through `raw`. A hand-written `<pre><code>` renders with no color, and `nav` never highlights markup already in a body, so it stays plain for good. A fenced block in a reference page's markdown is highlighted by `nav` on its own.
29
+
30
+ The quiz and teach-back block in `lesson-craft.md` always need `raw`, since their fixed contract is not a components concern. Give them the array's last `raw` entry, followed only by `refs`, and use `raw` nowhere the other types could carry the content.
25
31
 
26
- The quiz and the teach-back block in `lesson-craft.md` are the one case `raw` is always needed for, since their fixed contract is not a components concern. Give them the array's last `raw` entry, followed only by `refs`, rather than reaching for `raw` anywhere the other types could carry the content instead.
32
+ ### Citing
27
33
 
28
- A claim resting on a source carries a `cites` number, and the source goes in the lesson's one `refs` block rather than into the sentence. Draw every reference from what `canon teach resource --read` recorded, never from a lead, since a lead is a source nobody opened. Render refuses a cite no reference resolves, so number the list before citing into it.
34
+ A claim resting on a source carries a `cites` number, and the source goes in the lesson's one `refs` block rather than the sentence.
35
+
36
+ - Draw every reference from what `canon teach resource --read` recorded, never from a lead, since a lead is a source nobody opened
37
+ - Render refuses a cite no reference resolves, so number the list before citing into it
29
38
 
30
39
  ## Building the lesson body
31
40
 
32
- Step 4 of `teach-workspace` reads this section before writing a lesson, after the lesson verb has resolved its path and quiz order. Write the chrome as four empty marker pairs rather than composing it by hand: `<!-- canon:teach:style -->`/`<!-- /canon:teach:style -->` inside `<head>`, and `<!-- canon:teach:header -->`, `<!-- canon:teach:footnav -->`, and `<!-- canon:teach:scripts -->` each with its own close marker, in that order in `<body>`.
41
+ Step 4 of `teach-workspace` reads this section before writing a lesson, after the lesson verb has resolved its path and quiz order. Write the chrome as four empty marker pairs rather than composing it by hand.
42
+
43
+ - `<!-- canon:teach:style -->` and `<!-- /canon:teach:style -->` inside `<head>`
44
+ - `<!-- canon:teach:header -->`, `<!-- canon:teach:footnav -->`, and `<!-- canon:teach:scripts -->`, each with its own close marker, in that order in `<body>`
45
+
46
+ ### Render the body
33
47
 
34
- Build the authored `<h1>`, lede, body, and quiz as a JSON array of blocks rather than composing markup by hand, and render it through the verb rather than through a component import:
48
+ Build the authored `<h1>`, lede, body, and quiz as a JSON array of blocks, and render it through the verb rather than a component import:
35
49
 
36
50
  ```bash
37
51
  echo '[
@@ -45,9 +59,15 @@ echo '[
45
59
  ]' | canon teach render --json
46
60
  ```
47
61
 
48
- `## The block list` above states each type and where `raw` belongs. Take the call's `html` field and write it between the header's close marker and the footnav's open marker, and nothing else anywhere in the file.
62
+ `## The block list` above states each type and where `raw` belongs. Write the call's `html` field between the header's close marker and the footnav's open marker, and nothing else anywhere in the file.
49
63
 
50
- Report it rather than proceeding silently when the verb does not resolve, which is an installed CLI predating it, and never compose the lesson body by hand as a fallback. That is the state this section exists to end, and a target holds this skill body before it holds the verb, since a plugin skill reaches a target the moment it merges while the CLI reaches one only when a release publishes.
64
+ ### When a verb does not resolve
65
+
66
+ Every verb in this section can be missing from an installed CLI that predates it. Report that rather than proceeding silently, and never hand-compose the body, the chrome, or a palette as a fallback.
67
+
68
+ A hand-composed body is the state this section exists to end. A target holds this skill body before the verb, since a plugin skill reaches a target on merge while the CLI arrives only when a release publishes.
69
+
70
+ ### Fill the chrome
51
71
 
52
72
  Then run:
53
73
 
@@ -55,19 +75,27 @@ Then run:
55
75
  canon teach nav <topic> --json
56
76
  ```
57
77
 
58
- It fills every marker pair from what the workspace holds on disk: the embedded stylesheet, the header with its breadcrumb and jump menus, the prev/next footer nav, and the behavior scripts, and it rewrites the workspace's contents page and the teach-root listing in the same run. It refuses a lesson missing one of the four marker pairs by name rather than guessing at the boundary, so a marker dropped while writing the lesson is caught here rather than read back later as a lesson nothing links to. Report it rather than proceeding silently when the verb does not resolve, which is an installed CLI predating it, and never compose the chrome by hand as a fallback.
78
+ It fills every marker pair from what the workspace holds on disk, and rewrites the workspace's contents page and the teach-root listing in the same run.
59
79
 
60
- Seed the stylesheet through the verb rather than authoring a palette, on the first lesson in a workspace:
80
+ - The markers take the embedded stylesheet, the header with its breadcrumb and jump menus, the prev and next footer nav, and the behavior scripts
81
+ - It refuses a lesson missing a marker pair by name rather than guessing the boundary, so a dropped marker is caught here rather than read back as a lesson nothing links to
82
+
83
+ ### Seed the stylesheet
84
+
85
+ On the first lesson in a workspace, seed the stylesheet through the verb rather than authoring a palette:
61
86
 
62
87
  ```bash
63
88
  canon teach stylesheet <topic> --json
64
89
  ```
65
90
 
66
- It writes the design tokens as custom properties and the components built on them, from the one source every other rendered surface reads. Add lesson rules under the seed and reach a value through its property rather than restating the hex, which is what let each workspace fork the palette from every other. It refuses to overwrite, so running it again on a workspace that has grown its own rules is safe and reports `written` as false.
91
+ It writes the design tokens as custom properties and the components built on them, from the one source every rendered surface reads.
92
+
93
+ - Add lesson rules under the seed, and reach a value through its property rather than restating the hex, which is how each workspace forked the palette
94
+ - It refuses to overwrite, so a rerun on a workspace with its own rules is safe and reports `written` as false
67
95
 
68
- Report it rather than proceeding silently when the verb does not resolve, which is an installed CLI predating it. Do not fall back to writing a palette by hand.
96
+ ### Add glossary terms
69
97
 
70
- Add every term the lesson defines to `GLOSSARY.md` through the verb, which places the entries alphabetically in the shape the standard fixes:
98
+ Add every term the lesson defines to `GLOSSARY.md` through the verb, which places entries alphabetically in the shape the standard fixes:
71
99
 
72
100
  ```bash
73
101
  canon teach glossary <topic> --json \
@@ -75,4 +103,4 @@ canon teach glossary <topic> --json \
75
103
  --first-seen <the lesson or reference page this batch comes from>
76
104
  ```
77
105
 
78
- `--term` repeats and one call writes the file once, which is what keeps a batch of terms from racing on it. A term already defined is refused, since a definition the subject has moved under is a revision of the entry rather than a second one.
106
+ `--term` repeats and one call writes the file once, which keeps a batch of terms from racing on it. A term already defined is refused, since a definition the subject has moved under is a revision of the entry rather than a second one.
@@ -25,7 +25,8 @@ A workspace accumulates lessons over weeks. The learner reads them as one body o
25
25
  - `canon teach nav` embeds and re-embeds the shared stylesheet into every lesson on each run, so growing that stylesheet, by promoting anything used a second time into it, is the only step left by hand.
26
26
  - Keep the structural furniture identical across lessons: where the title sits, where the quiz sits, what a correct answer looks like
27
27
  - Do not restate styles inside a lesson. A local override is a decision the next lesson has to either copy or contradict.
28
- - Refer to another lesson in the same workspace as "lesson" followed by its four-digit number, such as "lesson 0003", and let `canon teach nav` turn the mention into a link and report a number naming no lesson. A hand-written link is the one form nav never checks.
28
+ - Refer to another lesson in the workspace as "lesson" plus its four-digit number, such as "lesson 0003". `canon teach nav` links the mention and reports a number naming no lesson.
29
+ - Never hand-write a link to another lesson, the one form nav never checks
29
30
 
30
31
  ## Typography
31
32
 
@@ -51,7 +52,7 @@ Decide the figure once per lesson, before writing the body.
51
52
 
52
53
  1. Name the one relationship, boundary, path, or before-and-after the lesson is about. A lesson about none ends here, with `Figure: none, <why it names no structure>` on the reply.
53
54
  2. Hold it against the bar in `${CLAUDE_SKILL_DIR}/../draft-figure/references/figures.md`, which also carries the drawing rules.
54
- 3. Draw it hand-written through `canon:draft-figure` into a `raw` block before the quiz, never inside it or after its feedback, which breaks the stepper's gating. Report it, never draw it yourself, if the skill does not resolve.
55
+ 3. Draw it through `canon:draft-figure` into a `raw` block before the quiz, never inside it or after its feedback, which breaks the stepper's gating. Report an unresolved skill rather than drawing.
55
56
  4. Or write one line on why prose carries it better, such as a structure an earlier lesson gave.
56
57
 
57
58
  A structure lesson takes step 3 or 4, never neither. Put the outcome on the reply's `Figure:` line, off the lesson page. Never draw to satisfy the step, or on every section (`## Restraint`).
@@ -61,11 +62,13 @@ A structure lesson takes step 3 or 4, never neither. Put the outcome on the repl
61
62
  The quiz is the retrieval, so a leak in its construction turns it into a reading test.
62
63
 
63
64
  - Write every option to the same length, in words and in characters. A longer option reads as the considered one.
64
- - Write the correct option first and let the ordering verb place it. Position is not the author's to pick: an author who varies it by hand still varies it by judgment, and the judgment settles on the first slot.
65
+ - Write the correct option first and let the ordering verb place it. An author varying position by hand varies it by judgment, which settles on the first slot.
65
66
  - Make each wrong option a misconception someone actually holds. An obviously wrong option removes itself and shrinks the question.
66
- - Write one feedback block per question, covering the correct option and why each distractor fails, saying why rather than whether. One block reads as an explanation where a block per option reads as four verdicts, and only the explanation names what separates a wrong option from the answer.
67
+ - Write one feedback block per question, covering the correct option and why each distractor fails, saying why rather than whether
67
68
  - Give feedback after the attempt, never alongside the question
68
69
 
70
+ One feedback block reads as an explanation where a block per option reads as four verdicts, and only the explanation names what separates a wrong option from the answer.
71
+
69
72
  ### The markup
70
73
 
71
74
  The quiz shape is a contract rather than a convention. `canon teach nav` splices a stepper into the lesson that gates on exactly these class names and this nesting, so a quiz written in any other shape renders with every question on screen at once and nothing reports it.
@@ -85,7 +88,9 @@ The quiz shape is a contract rather than a convention. `canon teach nav` splices
85
88
  - Mark the correct option with `data-a="1"` and leave the others without the attribute
86
89
  - Carry `data-k` on the `label`, since the workspace stylesheet renders the key letter from it
87
90
 
88
- #### Structure and the legacy button
91
+ ### Structure and the legacy button
92
+
93
+ The nesting matters as much as the class names.
89
94
 
90
95
  - Keep `.fb` the last child of its `.q`. The stepper reveals it as a direct child, and a feedback block nested deeper stays hidden.
91
96
  - Put the option text in a `<span>` inside the label rather than bare beside the input, so the workspace can lay the two out
@@ -97,9 +102,9 @@ The stepper hides and shows and sets nothing else, so how a selected option look
97
102
 
98
103
  A quiz is recognition and a teach-back is production, which is the form retrieval practice prefers. Carry both: the quiz places the learner against options someone could hold, and the teach-back is where the learner finds out what they cannot say without help.
99
104
 
100
- - Ask for an explanation to a named audience rather than for an answer. Asking the learner to explain to somebody who has never written a pattern why a lazy quantifier is not an optional one gives the explanation a floor, where asking them to explain lazy quantifiers gives it none.
105
+ - Ask for an explanation to a named audience rather than for an answer. Telling a non-programmer why a lazy quantifier is not an optional one has a floor, and "explain lazy quantifiers" has none.
101
106
  - Ask about the thing the lesson was for, not a detail beside it. A teach-back on a footnote tests attention rather than the material.
102
- - Carry a self-check the learner can open, as a `<details>` listing what a complete explanation covers, closed by default. A learner reading with no session in the room otherwise gets an ask they cannot grade, and closed leaks nothing.
107
+ - Carry a self-check as a closed `<details>` listing what a complete explanation covers. A learner with no session in the room otherwise cannot grade the ask, and closed leaks nothing.
103
108
  - Write the self-check as what the explanation has to cover rather than as the explanation itself. A model answer is read instead of produced, which turns the block back into reading.
104
109
 
105
110
  ```html