@erclx/canon 5.39.0 → 5.41.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 (162) hide show
  1. package/README.md +39 -22
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/auto-ship/REQUIREMENT.md +1 -0
  4. package/claude/skills/auto-ship/SKILL.md +6 -0
  5. package/claude/skills/auto-ship/references/fan-in.md +59 -0
  6. package/claude/skills/canvas-board/SKILL.md +1 -1
  7. package/claude/skills/create-skill/references/skill.md +1 -1
  8. package/claude/skills/decision-escalate/REQUIREMENT.md +1 -1
  9. package/claude/skills/decision-escalate/SKILL.md +1 -2
  10. package/claude/skills/draft-slides/SKILL.md +2 -3
  11. package/claude/skills/draft-slides/references/deck.md +183 -0
  12. package/claude/skills/git-branch/REQUIREMENT.md +17 -6
  13. package/claude/skills/git-branch/SKILL.md +63 -7
  14. package/claude/skills/git-pr/references/evidence.md +2 -2
  15. package/claude/skills/git-pr/references/final-command.md +1 -1
  16. package/claude/skills/git-pr/references/labels.md +1 -1
  17. package/claude/skills/git-pr/references/publish.md +1 -1
  18. package/claude/skills/plan-feature/references/batches.md +48 -1
  19. package/claude/skills/plan-feature/references/plan.md +2 -0
  20. package/claude/skills/plan-intake-answer/REQUIREMENT.md +1 -1
  21. package/claude/skills/plan-intake-answer/SKILL.md +1 -2
  22. package/claude/skills/review-address/references/rebase-conflicts.md +7 -0
  23. package/claude/skills/review-ui/SKILL.md +3 -0
  24. package/claude/skills/review-ui/references/close-out.md +47 -0
  25. package/claude/skills/review-ui/references/post.md +3 -1
  26. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -0
  27. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
  28. package/claude/skills/role-worker/REQUIREMENT.md +1 -0
  29. package/claude/skills/role-worker/SKILL.md +9 -0
  30. package/claude/skills/task-board/references/board.md +1 -1
  31. package/claude/skills/task-board/references/tasks.md +1 -1
  32. package/claude/skills/teach-workspace/REQUIREMENT.md +1 -1
  33. package/claude/skills/teach-workspace/SKILL.md +1 -2
  34. package/claude/skills/ux-measure/SKILL.md +1 -1
  35. package/docs/agents/audits.md +61 -60
  36. package/docs/agents/context-audit.md +40 -58
  37. package/docs/agents/index.md +5 -51
  38. package/docs/agents/install-and-sync.md +1 -0
  39. package/docs/agents/markdown-audit.md +53 -99
  40. package/docs/agents/records.md +37 -79
  41. package/docs/agents/sessions-transfer.md +26 -70
  42. package/docs/target/index.md +0 -1
  43. package/docs/target/migrations.md +57 -66
  44. package/docs/target/projects.md +36 -78
  45. package/docs/target/scenarios.md +9 -19
  46. package/docs/target/sync.md +27 -68
  47. package/docs/workflow/ai-workflow.md +22 -68
  48. package/docs/workflow/operating-model.md +32 -186
  49. package/docs/workflow/parallel-features.md +30 -66
  50. package/docs/workflow/skill-map-on-request.md +22 -24
  51. package/docs/workflow/skill-map.md +72 -76
  52. package/docs/workflow/visual-design-workflow.md +25 -118
  53. package/docs/workflow/zshrc-aliases.md +18 -37
  54. package/package.json +1 -1
  55. package/src/canvas/client/inspector/fields.css +4 -0
  56. package/src/canvas/index.html +6 -0
  57. package/src/claude/cases/git.ts +4 -0
  58. package/src/claude/skills-reach.ts +2 -2
  59. package/src/cli/ui.ts +3 -1
  60. package/src/commands/canvas.ts +23 -0
  61. package/src/commands/capture.ts +14 -0
  62. package/src/commands/claude.ts +13 -0
  63. package/src/commands/demo.ts +22 -0
  64. package/src/commands/deps.ts +6 -0
  65. package/src/commands/driver.ts +28 -2
  66. package/src/commands/gov.ts +52 -18
  67. package/src/commands/intake.ts +17 -0
  68. package/src/commands/labels.ts +38 -5
  69. package/src/commands/pr.ts +192 -52
  70. package/src/commands/records.ts +19 -3
  71. package/src/commands/secrets.ts +6 -0
  72. package/src/commands/sessions.ts +8 -0
  73. package/src/commands/tasks.ts +52 -0
  74. package/src/commands/teach.ts +13 -0
  75. package/src/commands/upstream.ts +25 -1
  76. package/src/commands/worktrees.ts +5 -0
  77. package/src/context/audit.ts +22 -4
  78. package/src/context/folders.ts +1 -1
  79. package/src/context/shape-gated.ts +5 -2
  80. package/src/counts/scan.ts +1 -1
  81. package/src/design/components.ts +1 -1
  82. package/src/design/document.ts +1 -1
  83. package/src/design/tokens.ts +1 -1
  84. package/src/gate/boundaries.ts +4 -1
  85. package/src/gate/measures.ts +1 -1
  86. package/src/gate/stages.ts +7 -7
  87. package/src/git/env.ts +6 -0
  88. package/src/markdown/scope.ts +11 -19
  89. package/src/pr/frames.ts +53 -9
  90. package/src/pr/lift.ts +153 -19
  91. package/src/pr/painted.ts +87 -0
  92. package/src/records/validate.ts +1 -0
  93. package/src/roots/record.ts +18 -1
  94. package/src/sandbox/census.ts +1 -1
  95. package/src/sandbox/headless/run.ts +3 -2
  96. package/src/sandbox/headless/watch.ts +3 -4
  97. package/src/slides/convert/master.ts +3 -0
  98. package/src/sync/format.ts +46 -0
  99. package/src/sync/stamp.ts +6 -0
  100. package/src/tasks/answers.ts +79 -2
  101. package/src/tasks/fan-in.ts +176 -0
  102. package/src/tasks/stack.ts +1 -1
  103. package/tooling/astro/reference.md +80 -39
  104. package/tooling/base/reference.md +63 -32
  105. package/tooling/base/seeds/canon/context/ci.md +7 -15
  106. package/tooling/base/seeds/canon/context/development.md +14 -14
  107. package/tooling/claude/reference.md +40 -22
  108. package/tooling/cloudflare/reference.md +31 -12
  109. package/tooling/go/reference.md +47 -23
  110. package/tooling/nextjs/reference.md +52 -19
  111. package/tooling/php/reference.md +35 -16
  112. package/tooling/python/reference.md +47 -30
  113. package/tooling/shell/reference.md +6 -5
  114. package/tooling/vite-react/reference.md +49 -21
  115. package/tooling/web/reference.md +131 -71
  116. package/docs/agents/canvas.md +0 -86
  117. package/docs/agents/capture.md +0 -65
  118. package/docs/agents/census.md +0 -23
  119. package/docs/agents/comments.md +0 -34
  120. package/docs/agents/context-audit-checks.md +0 -108
  121. package/docs/agents/context-classify.md +0 -101
  122. package/docs/agents/counts.md +0 -74
  123. package/docs/agents/demo.md +0 -140
  124. package/docs/agents/driver.md +0 -103
  125. package/docs/agents/gate.md +0 -90
  126. package/docs/agents/hooks.md +0 -78
  127. package/docs/agents/intake.md +0 -81
  128. package/docs/agents/key-changes.md +0 -116
  129. package/docs/agents/label-coverage.md +0 -86
  130. package/docs/agents/markdown-audit-density.md +0 -39
  131. package/docs/agents/markdown-audit-weight.md +0 -83
  132. package/docs/agents/pr-evidence-layout.md +0 -70
  133. package/docs/agents/pr-evidence.md +0 -273
  134. package/docs/agents/pr-frames.md +0 -131
  135. package/docs/agents/pr-lift.md +0 -132
  136. package/docs/agents/pr-local.md +0 -111
  137. package/docs/agents/pr-preview.md +0 -155
  138. package/docs/agents/pr-reads.md +0 -111
  139. package/docs/agents/pr-tick.md +0 -72
  140. package/docs/agents/records-push.md +0 -61
  141. package/docs/agents/records-stale.md +0 -40
  142. package/docs/agents/records-validate.md +0 -57
  143. package/docs/agents/restated.md +0 -83
  144. package/docs/agents/routing.md +0 -61
  145. package/docs/agents/rule-citations.md +0 -90
  146. package/docs/agents/sandbox.md +0 -126
  147. package/docs/agents/sessions.md +0 -128
  148. package/docs/agents/skills-audit.md +0 -120
  149. package/docs/agents/skills-reach.md +0 -64
  150. package/docs/agents/slides.md +0 -176
  151. package/docs/agents/state-scoped-risk.md +0 -105
  152. package/docs/agents/superseded.md +0 -85
  153. package/docs/agents/tasks-archive.md +0 -83
  154. package/docs/agents/tasks-plans.md +0 -152
  155. package/docs/agents/tasks-validate.md +0 -133
  156. package/docs/agents/tasks.md +0 -123
  157. package/docs/agents/teach-authoring.md +0 -118
  158. package/docs/agents/teach.md +0 -157
  159. package/docs/agents/test-order.md +0 -58
  160. package/docs/agents/upstream.md +0 -94
  161. package/docs/agents/worktrees.md +0 -118
  162. package/docs/target/standards-moved.md +0 -34
package/README.md CHANGED
@@ -12,7 +12,6 @@
12
12
  [![License MIT](https://img.shields.io/npm/l/@erclx/canon)](LICENSE)
13
13
 
14
14
  canon is a CLI and Claude Code plugin that runs your agent's work from plan to merge, holds it to the practices that make code worth keeping, and installs the same setup in every repository.
15
-
16
15
  **[See it run at canon.erclx.dev](https://canon.erclx.dev)**
17
16
 
18
17
  </div>
@@ -21,9 +20,13 @@ That page is one real session the toolkit ran on itself, and every count on it i
21
20
 
22
21
  ## Why
23
22
 
24
- If an agent writes code for you in more than one repository, this is for you. canon runs that work from plan to merge. A planner writes the plan, a worker builds it on its own branch, and a separate session reviews the pull request. It holds the work to a practice, with rules that load when a matching path is edited and checks that fail when a rule is ignored. Both run on one setup, installed the same way in every repository you keep.
23
+ If an agent writes code for you in more than one repository, this is for you. canon runs that work from plan to merge. A planner writes the plan, a worker builds it on its own branch, and a separate session reviews the pull request.
24
+
25
+ It holds the work to a practice, with rules that load when a matching path is edited and checks that fail when a rule is ignored. Both run on one setup, installed the same way in every repository you keep.
25
26
 
26
- That shared setup is what the rest of this page describes, and it is the part that drifts without a source. Every AI coding setup accumulates the same assets. Prompts to reuse, rules agents should follow, slash commands, skills, seed docs, sync scripts. Once you have enough projects, your copies drift and your agents stop getting consistent signals.
27
+ ### The shared setup
28
+
29
+ That shared setup is what the rest of this page describes, and it drifts without a source. Every AI coding setup accumulates the same assets. Prompts to reuse, rules agents should follow, slash commands, skills, seed docs, sync scripts. Once you have enough projects, your copies drift and your agents stop getting consistent signals.
27
30
 
28
31
  Three design choices shape the toolkit.
29
32
 
@@ -61,17 +64,21 @@ bun install --global @erclx/canon
61
64
 
62
65
  Each domain has a canonical source in this repo and a thin install or sync CLI on your side. The domains split on one line: some are copied into your project and become yours to edit, and some are never copied at all.
63
66
 
64
- | Domain | What it is | How it reaches you |
65
- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
66
- | Plugin skills | Skills that plan a feature, review a diff, sync the planning docs, and run the ship chain | Loaded live from the plugin, never copied |
67
- | Governance | Coding and authoring rules that load into a Claude session when a matching path is edited | Installed per project by `canon gov install`, refreshed by sync |
68
- | Tooling stacks | Golden configs, seeds, and a reference per framework | Laid down by `canon init`, reconciled by `canon tooling sync` |
69
- | Design system | Ships a `DESIGN.md` intent format that points at the project's stylesheet for values, a skill that drafts one from an existing project or from scratch, and a render command | `canon design render` |
70
- | Slides | A folder of HTML slides per deck, drawn in the project's tokens and rebuilt as editable shapes | `canon slides render` writes PowerPoint |
71
- | Teach | Lessons, sources, and a glossary in one workspace, rendered to a small site | `canon teach` |
72
- | Canvas | Pages of HTML frames on a local pan and zoom surface, each frame drawn with the project's tokens | `canon canvas serve` |
73
- | Transcripts | A YouTube transcript with metadata frontmatter | `canon transcripts <url>` writes it into any repo |
74
- | Sandbox | Scenario scaffolds that provision an isolated project state for verifying each domain flow | `canon sandbox` |
67
+ ### Domain catalog
68
+
69
+ | Domain | What it is | How it reaches you |
70
+ | -------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
71
+ | Plugin skills | Skills that plan a feature, review a diff, sync the planning docs, and run the ship chain | Loaded live from the plugin, never copied |
72
+ | Governance | Coding and authoring rules that load into a Claude session when a matching path is edited | Installed per project by `canon gov install`, refreshed by sync |
73
+ | Tooling stacks | Golden configs, seeds, and a reference per framework | Laid down by `canon init`, reconciled by `canon tooling sync` |
74
+ | Design system | Ships a `DESIGN.md` intent format pointing at your stylesheet for values, a skill that drafts one, and a render command | `canon design render` |
75
+ | Slides | A folder of HTML slides per deck, drawn in the project's tokens and rebuilt as editable shapes | `canon slides render` writes PowerPoint |
76
+ | Teach | Lessons, sources, and a glossary in one workspace, rendered to a small site | `canon teach` |
77
+ | Canvas | Pages of HTML frames on a local pan and zoom surface, each frame drawn with the project's tokens | `canon canvas serve` |
78
+ | Transcripts | A YouTube transcript with metadata frontmatter | `canon transcripts <url>` writes it into any repo |
79
+ | Sandbox | Scenario scaffolds that provision an isolated project state for verifying each domain flow | `canon sandbox` |
80
+
81
+ ### How domains reach you
75
82
 
76
83
  Those domains reach you in three shapes. A tooling stack lands as real files under version control, because a config is something your build reads and your project owns. A plugin skill and the references it carries are never copied, so there is no copy in your repo to drift from this one.
77
84
 
@@ -117,6 +124,8 @@ The first two update the skills, the third updates the CLI, and they move indepe
117
124
 
118
125
  `canon upgrade` reads the package manager off its own install path and reinstalls with that one, so you don't have to remember which put it there. It names what it detected before it runs anything, and it refuses a source checkout rather than reinstalling over your clone.
119
126
 
127
+ ### Checking for drift
128
+
120
129
  You don't have to wait until something breaks to find out you're behind. `canon sync --check` and `canon claude skills drift` both report the installed version against the newest published one, and neither changes its exit code over it, so an offline machine reads unknown rather than red.
121
130
 
122
131
  To stop doing this by hand, turn auto-update on once under `/plugin` in the Marketplaces tab. Confirm what you are running with `canon --version` and `claude plugin list`.
@@ -127,6 +136,8 @@ Working on the toolkit starts from a clone. Running the CLI doesn't, since it in
127
136
 
128
137
  ### Prerequisites
129
138
 
139
+ Bun, Git, and a shell are required, and the GitHub CLI is optional.
140
+
130
141
  - [Bun](https://bun.sh) for the CLI runtime and scripts
131
142
  - [Git](https://git-scm.com) with worktree support
132
143
  - [GitHub CLI](https://cli.github.com) (optional) for ship flows
@@ -141,7 +152,10 @@ bun install
141
152
  bun run bootstrap
142
153
  ```
143
154
 
144
- The script is idempotent, so re-run it after pulling upstream changes without duplicating anything. That also means it leaves an alias block you already have alone rather than refreshing it, so an alias added upstream needs the block deleted and the script re-run. It confirms the install by resolving `canon --help` on the last step. See [zshrc aliases](docs/workflow/zshrc-aliases.md) for what each alias does, how to pick up a new one, and how to opt out of the block.
155
+ The script is idempotent, so re-run it after pulling upstream changes without duplicating anything. It confirms the install by resolving `canon --help` on the last step.
156
+
157
+ - It leaves an alias block you already have alone rather than refreshing it, so an alias added upstream needs the block deleted and the script re-run.
158
+ - [Zshrc aliases](docs/workflow/zshrc-aliases.md) covers what each alias does, how to pick up a new one, and how to opt out of the block.
145
159
 
146
160
  With the CLI linked, scaffold a fresh project.
147
161
 
@@ -151,9 +165,12 @@ git init
151
165
  canon init
152
166
  ```
153
167
 
154
- `canon init` installs base tooling configs, Claude seeds, and governance rules in one pass, and scaffolds a `.claude/wiki/` stub for your project's own reference pages. Governance defaults to the `base` stack, so a bare init lands the workflow and doc-authoring rules in `.claude/rules/`. Each rule names the skill it loads, so no corpus is copied into your project.
168
+ `canon init` installs base tooling configs, Claude seeds, and governance rules in one pass, and scaffolds a `.claude/wiki/` stub for your project's own reference pages.
155
169
 
156
- Pass `--stack <name>` for a language stack such as `node` or `python`, or `--skip governance` to leave rules out. Run `canon tooling list --json` to see the catalog.
170
+ - Governance defaults to the `base` stack, so a bare init lands the workflow and doc-authoring rules in `.claude/rules/`.
171
+ - Each rule names the skill it loads, so no corpus is copied into your project.
172
+ - Pass `--stack <name>` for a language stack such as `node` or `python`, or `--skip governance` to leave rules out.
173
+ - Run `canon tooling list --json` to see the catalog.
157
174
 
158
175
  For the full journey from scaffold through adding a domain later to syncing upstream drift, see [target projects](docs/target/projects.md).
159
176
 
@@ -161,13 +178,13 @@ For the full journey from scaffold through adding a domain later to syncing upst
161
178
 
162
179
  Each domain carries an entry written for someone maintaining the toolkit rather than installing it. These paths resolve in a clone only. The published package ships `docs` and not `.claude`, so an installed copy does not carry them.
163
180
 
164
- - [Claude Code plugin](canon/context/claude-plugin/index.md)
165
- - [Governance rules](canon/context/governance/index.md)
166
- - [Tooling stacks](canon/context/tooling/index.md)
167
- - [Design system](canon/context/design.md)
181
+ - [Claude Code plugin](canon/context/surfaces/plugin.md)
182
+ - [Governance rules](canon/context/surfaces/governance.md)
183
+ - [Tooling stacks](canon/context/surfaces/tooling.md)
184
+ - [Design system](canon/context/features/design.md)
168
185
  - [Slides](canon/context/features/slides.md)
169
186
  - [Transcripts](canon/context/features/transcripts.md)
170
- - [Sandbox](canon/context/sandbox/index.md)
187
+ - [Sandbox](canon/context/development/sandbox.md)
171
188
 
172
189
  ## Contributing
173
190
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
4
- "version": "5.39.0",
4
+ "version": "5.41.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -19,6 +19,7 @@ Without this skill, the run from an approved plan to an open pull request is a c
19
19
  - Open the pull request as a draft before the continuous integration watch begins, since a pull request marked after it is mergeable for the length of the run
20
20
  - Continue past a produced UI checklist rather than stopping on it, and report its unchecked boxes in the closing block, since the draft mark already holds the merge
21
21
  - Prove the draft target's head is the current branch and its state is open before marking it, and stop with nothing marked on a mismatch, since the number is retyped between steps and a draft mark on a release pull request holds that release
22
+ - Stop a fan-in member after its verify and test order with a push and a done message rather than a pull request, and open an integrator's build by merging the member branches, since parallel batches sharing edge files put every open sibling into conflict on each merge
22
23
  - Name the recovery for the stop it took, since the value of stopping is that the user knows where to resume
23
24
 
24
25
  ## Must not
@@ -73,6 +73,8 @@ When this run decides against a suggestion, rewrite its line as `overridden at e
73
73
 
74
74
  Load `canon:build-in-slices`, reporting it when it does not resolve, and implement only what the plan describes. Do not expand scope, refactor neighbors, or touch files outside the plan's "Files to touch" list without reason.
75
75
 
76
+ When the plan's `**Constraints:**` carry a `Fans in` bullet, this run is a fan-in integrator, so open this step by merging the member branches, per `${CLAUDE_SKILL_DIR}/references/fan-in.md`.
77
+
76
78
  ## Step 3: verify
77
79
 
78
80
  Run the verify commands defined in `CLAUDE.md` (lint, typecheck, tests). On failure:
@@ -97,6 +99,10 @@ The verb reads git history rather than the working tree, so it catches a violati
97
99
 
98
100
  Never read a missing subcommand as clean. Read `${CLAUDE_SKILL_DIR}/references/verb-absent.md` on meeting one.
99
101
 
102
+ ### A fan-in member stops here
103
+
104
+ When the plan's `**Constraints:**` carry a `Fan-in member of` bullet, push the branch, announce it done, and stop before Step 5. A member opens no pull request, since its integrator ships the whole set. Read `${CLAUDE_SKILL_DIR}/references/fan-in.md` for the push and the message.
105
+
100
106
  ## Step 5: UI checklist (conditional)
101
107
 
102
108
  If the diff touches UI files (JSX, TSX, Vue, Svelte, HTML, or CSS under `src/`), invoke `canon:ui-checklist`, then continue to Step 6 whatever it returns, in every run. The Step 6 draft mark holds the merge, so a stop here protects nothing.
@@ -0,0 +1,59 @@
1
+ ---
2
+ title: Fan-in member exit and integrator merge
3
+ description: How auto-ship stops a fan-in member after its push and how an integrator merges the member branches before its own build, so a set of parallel batches ships as one pull request
4
+ ---
5
+
6
+ # Fan-in
7
+
8
+ The session reads this file at one of two points in `auto-ship`. A member reads `## Member exit` after Step 4, and an integrator reads `## Integrator merge` at the opening of Step 2. When to fan in and how a plan declares its side are in `${CLAUDE_SKILL_DIR}/../plan-feature/references/batches.md`.
9
+
10
+ ## Member exit
11
+
12
+ Step 3's verify and Step 4's test order have already run, so the branch is green and committed. The member ends here rather than at a pull request.
13
+
14
+ 1. Push the branch with `git push -u origin <branch>`. The pre-push hook runs the project check, and a member that fails it stops the way Step 3 does.
15
+ 2. Read the head with `git rev-parse HEAD` once the push returns.
16
+ 3. Announce the batch done to the controlling session through `canon:session-relay`, carrying the branch, the head commit, the integrator the plan names, and any row this build turned up.
17
+ 4. Stop. Run no UI checklist, no `git-ship`, and no draft mark.
18
+
19
+ The pushed branch is the done signal the integrator's gate reads, so push once, at the end, and never mid-build. The announcement carries no reach, since members of one set share files by design and the integrator's announcement carries the reach for the whole set.
20
+
21
+ A fix the integrator needs after this point lands on the integrator's branch, since a member has no pull request to fix it on.
22
+
23
+ ## Integrator merge
24
+
25
+ The members are pushed before this run starts, since `canon tasks plan-answers` holds an integrator until every member branch is on origin. Confirm that before merging anything:
26
+
27
+ ```bash
28
+ git fetch origin --quiet
29
+ canon tasks plan-answers <plan> --json
30
+ ```
31
+
32
+ Stop on `launchable: false`, reporting each `open` entry, since a `Fan-in pending` entry names a member that has not pushed and a `Fan-in set` entry names a set whose sides disagree. Then merge each member in the order the `Fans in` bullet lists:
33
+
34
+ ```bash
35
+ git merge --no-ff --no-edit origin/<member branch>
36
+ ```
37
+
38
+ ### Conflicts and the order
39
+
40
+ `--no-ff` keeps one merge commit per member, so `git log --first-parent` shows the reviewer one member at a time. Never squash or fast-forward a member in.
41
+
42
+ - Resolve each conflict by `## Resolving a conflict` in `${CLAUDE_SKILL_DIR}/../review-address/references/rebase-conflicts.md`
43
+ - Stop on a hunk needing a decision with `git merge --abort` in place of the rebase abort that file names
44
+ - Regenerate the indexes once, after the last merge, rather than resolving a generated file by hand at each one
45
+ - Run `git config rerere.enabled true` before the first merge, since a stale integrator rebases with `--rebase-merges` per the same file and that replays each merge without its recorded resolution
46
+
47
+ Continue Step 2 with the integrator plan's own `**Files to touch:**`, then the rest of the chain as an ordinary worker.
48
+
49
+ ### What the pull request carries
50
+
51
+ Say in the pull request body that each member is one merge commit and name the members in merge order. Once the pull request merges, delete each member's branch on origin, its worktree, and its local branch:
52
+
53
+ ```bash
54
+ git push origin --delete <member branch>
55
+ git worktree remove <member worktree>
56
+ git branch -D <member branch>
57
+ ```
58
+
59
+ Read each member's worktree path off `git worktree list --porcelain` by its branch. The integrator holds the member list, so the deletion is its step rather than a sweep's. No member opens a pull request, so `canon worktrees reclaim` never reads a member's branch as merged and reports a leftover worktree as unmerged work on every run.
@@ -9,7 +9,7 @@ metadata:
9
9
 
10
10
  Drive the canvas the operator is looking at. A page is a folder of HTML frames and a frame is one file, so a session draws by writing files and the operator sees each write land in the browser. The content format is fixed by `${CLAUDE_SKILL_DIR}/references/canvas.md`. Read it before writing a frame.
11
11
 
12
- Every verb below is `canon canvas`. Run `canon docs canvas` for the full verb reference, its records, and its refusals.
12
+ Every verb below is `canon canvas`. Run `canon canvas --help` for the verbs and their refusals, and read `${CLAUDE_SKILL_DIR}/references/canvas.md` for the content format.
13
13
 
14
14
  ## Guards
15
15
 
@@ -104,7 +104,7 @@ allowed-tools: <tools required>
104
104
 
105
105
  - **`name`** (required): kebab-case, matches folder name, no spaces or capitals
106
106
  - **`description`** (required): what it does + when to use it, under 1024 chars, no XML tags
107
- - **`disable-model-invocation: true`**: user-invoked only, Claude will not auto-trigger. Set it on a skill that starts a process the operator owns, never on one reachable by a matched trigger.
107
+ - **`disable-model-invocation: true`**: Claude will not auto-trigger, and the `Skill` tool also refuses the operator's own request made in words. Set it only on a skill run by slash command alone. A skill wanting just the first effect writes `Do NOT auto-trigger` in its description.
108
108
  - **`allowed-tools`**: restrict tool access when the skill is active
109
109
  - **`metadata`**: optional key-value pairs (`author`, `version`, `mcp-server`)
110
110
  - **`metadata.family`** (required on a skill under `claude/skills/`): the skill map group the skill's row sits under, as one key from `families` in `canon claude skills list --json`. A skill under `.claude/skills/` takes none.
@@ -41,4 +41,4 @@ The opposite failure is the one that ships silently. A session that takes a pref
41
41
 
42
42
  - Making the ordinary judgment call, which is a pick plus a one-sentence tradeoff and needs no surface
43
43
  - Writing the decision into a plan, task, or context entry, which each owning standard governs and this skill only routes to
44
- - Deciding when to fire. The skill is user-invoked through `disable-model-invocation`, so escalating is the operator's call rather than a description match.
44
+ - Deciding when to fire. The description states `Do NOT auto-trigger`, so escalating is the operator's call rather than a description match.
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: decision-escalate
3
- description: Collects every open decision whose answer turns on the operator's preference, since only they know what they need, puts them as one batch of questions each carrying options and a recommended default, then waits. Use when asked to "escalate this", "ask me the open questions", "batch the open decisions", "what do you need from me", or "stop and ask before you pick". Do NOT use for a judgment call the session can settle, which is a pick with the tradeoff stated in one sentence.
4
- disable-model-invocation: true
3
+ description: Collects every open decision whose answer turns on the operator's preference, since only they know what they need, puts them as one batch of questions each carrying options and a recommended default, then waits. Use when asked to "escalate this", "ask me the open questions", "batch the open decisions", "what do you need from me", or "stop and ask before you pick". Do NOT use for a judgment call the session can settle, which is a pick with the tradeoff stated in one sentence. Do NOT auto-trigger.
5
4
  metadata:
6
5
  family: decide
7
6
  ---
@@ -11,16 +11,15 @@ Draw each slide as an HTML file in a deck folder, then shell out to `canon slide
11
11
 
12
12
  ## Read the decks and the format
13
13
 
14
- Run both first. Never hardcode a deck name, an attribute, or a chart type. The CLI is the source of truth for each.
14
+ Run the list first, then read the format. Never hardcode a deck name, an attribute, or a chart type.
15
15
 
16
16
  ```bash
17
17
  canon slides list --json 2>/dev/null
18
- canon docs slides
19
18
  ```
20
19
 
21
20
  The list returns one row per deck, carrying its `name`, `title`, slide count, and `path`. When the request names a deck the list holds, edit that folder. Otherwise pick a new kebab-case name no row carries.
22
21
 
23
- The docs page states what `deck.json` holds, what each slide declares on its `<body>`, the motion attributes, speaker notes, charts, and what each refusal reports.
22
+ Read `${CLAUDE_SKILL_DIR}/references/deck.md`. It states what `deck.json` holds, what each slide declares on its `<body>`, the motion attributes, speaker notes, and charts. Each refusal prints as a `✗` line from the render.
24
23
 
25
24
  ## Draw the deck
26
25
 
@@ -0,0 +1,183 @@
1
+ ---
2
+ title: Deck reference
3
+ description: The deck folder, the deck.json fields, the attributes a slide sets on its body, motion, the master's token roles, speaker notes, and charts
4
+ ---
5
+
6
+ # Deck reference
7
+
8
+ Applies to the slides under `.canon/slides/<deck>/`, which `canon slides render <deck>` turns into an editable PowerPoint file. Each slide describes its own shapes. What belongs to the whole deck sits in `deck.json`.
9
+
10
+ ## Contents
11
+
12
+ - [The deck folder](#the-deck-folder)
13
+ - [The deck file](#the-deck-file)
14
+ - [What each slide declares](#what-each-slide-declares)
15
+ - [Motion](#motion)
16
+ - [The master](#the-master)
17
+ - [Speaker notes](#speaker-notes)
18
+ - [Charts](#charts)
19
+
20
+ ## The deck folder
21
+
22
+ | Path | Holds |
23
+ | -------------------------------- | ---------------------------------------------------- |
24
+ | `.canon/slides/<deck>/*.html` | the slides, rendered in filename order |
25
+ | `.canon/slides/<deck>/deck.json` | what belongs to the whole deck |
26
+ | `.canon/slides/<deck>/assets/` | images the slides reference by a relative path |
27
+ | `.canon/slides/layouts/` | the project's shared layouts, never listed as a deck |
28
+
29
+ A folder holding no `.html` file is not a deck. `render` with no name takes the only deck, and refuses with the list when there are several. It also takes a folder path outside `.canon/slides/`.
30
+
31
+ ## The deck file
32
+
33
+ `deck.json` is optional. Without it the deck takes the defaults below.
34
+
35
+ | Field | Default | What it sets |
36
+ | -------------- | ------------------------------ | ------------------------------------------------- |
37
+ | `title` | the folder name | the file's title and the default footer text |
38
+ | `header` | off | a band at the top, with `left`, `center`, `right` |
39
+ | `footer` | on, with the title on the left | a band at the bottom, with the same three slots |
40
+ | `slideNumbers` | `true` | the number at the right of the footer |
41
+ | `mark` | none | an image path, relative to the folder, top right |
42
+ | `fonts` | none | faces to embed in the file |
43
+
44
+ ```json
45
+ {
46
+ "title": "Q3 review",
47
+ "header": { "left": "Acme" },
48
+ "footer": { "center": "Internal" },
49
+ "mark": "assets/mark.png",
50
+ "fonts": [
51
+ { "family": "Inter", "path": "fonts/Inter-Regular.ttf" },
52
+ { "family": "Inter", "weight": 700, "path": "fonts/Inter-Bold.ttf" }
53
+ ]
54
+ }
55
+ ```
56
+
57
+ ### Bands and the mark
58
+
59
+ A band takes `show` and the three slots. A declared band merges over its default, so `"footer": { "center": "Internal" }` keeps the title on the left. Declaring any slot turns a band on unless it also says `"show": false`. Set a slot to `""` to empty it.
60
+
61
+ - Slide numbers hold the footer's right slot and show only where the footer does. Set `slideNumbers` to `false` to use `footer.right`.
62
+ - Prefer a raster mark, since an SVG mark lands as a vector with a broken fallback.
63
+
64
+ ### Embedded faces
65
+
66
+ Each `fonts` entry names one TrueType or OpenType file inside the deck folder:
67
+
68
+ | Key | Default | Rule |
69
+ | -------- | -------- | ------------------------------------------------------------------- |
70
+ | `family` | required | match the family the slides set |
71
+ | `weight` | `400` | `600` and above fills the bold slot, anything lighter fills regular |
72
+ | `style` | `normal` | `normal` or `italic` |
73
+ | `path` | required | relative to the folder, and has to stay inside it |
74
+
75
+ A face is left out, and the deck still written, when its license forbids embedding, when it is a variable font or a `woff` or `woff2` file, or when a second face lands in a filled slot. Supply static instances, one per weight, and the TrueType or OpenType file a `woff2` was compressed from.
76
+
77
+ ## What each slide declares
78
+
79
+ A slide sets its place in the deck through attributes on `<body>`.
80
+
81
+ | Attribute | Effect |
82
+ | --------------------------------------- | --------------------------------------------- |
83
+ | `data-section="Results"` | opens a section the slides after it stay in |
84
+ | `data-hidden` | hides the slide in the show |
85
+ | `data-header="off"` | drops the header on this slide |
86
+ | `data-footer="off"` | drops the footer and its number on this slide |
87
+ | `data-footer-left`, `-center`, `-right` | replaces that footer slot on this slide alone |
88
+ | `data-header-left`, `-center`, `-right` | replaces that header slot on this slide alone |
89
+
90
+ Once any slide opens a section, slides ahead of the first sit in a section named after the deck. A title the deck returns to after another section opens a new section numbered `Intro (2)`.
91
+
92
+ A slide dropping one band keeps the other from the master. A slide replacing a slot draws its band as its own text, so a later edit in the master view skips it.
93
+
94
+ ## Motion
95
+
96
+ | Attribute | Takes | Effect |
97
+ | -------------------------- | ------------------------------- | -------------------------------------- |
98
+ | `data-transition` | `fade`, `push`, `wipe`, `cover` | the transition into this slide |
99
+ | `data-transition-duration` | `500`, `500ms`, or `0.5s` | rounded to 0.5, 0.75, or 1 second |
100
+ | `data-enter` | `fade`, `fly`, `wipe`, `zoom` | an entrance played on a click |
101
+ | `data-enter-order` | a number | the click it plays on, lowest first |
102
+ | `data-enter-duration` | `500`, `500ms`, or `0.5s` | the entrance length, 500 ms by default |
103
+
104
+ `data-transition` and `data-transition-duration` go on `<body>`. The `data-enter` family goes on the element.
105
+
106
+ ```html
107
+ <body data-transition="fade">
108
+ <h1>Results</h1>
109
+ <div class="card" data-enter="fly" data-enter-order="1">…</div>
110
+ <div class="card" data-enter="fly" data-enter-order="2">…</div>
111
+ </body>
112
+ ```
113
+
114
+ - Each entrance is its own click, and entrances without an order play after the ordered ones, in page order.
115
+ - An entrance brings in every shape drawn from its element and the elements inside it, so a card's box and text arrive together.
116
+ - An inner element with its own `data-enter` waits for its own click.
117
+ - Fly comes in from the bottom and wipe from the left.
118
+
119
+ ## The master
120
+
121
+ The master takes its colors from the `<html>` element of the first slide, laid out with the project's token stylesheet injected:
122
+
123
+ | Source | Used for |
124
+ | -------------------------------------------- | ---------------- |
125
+ | `--color-background` | the slide ground |
126
+ | `--color-text` | the header text |
127
+ | `--color-muted` | the footer text |
128
+ | `--color-accent` | the slide number |
129
+ | `<body>` font family, ` Variable` suffix cut | the theme face |
130
+
131
+ A role the project declares no token for falls back to the slide body's own color and prints one `✗` line naming it. It never falls back to the toolkit's palette.
132
+
133
+ A slide whose `<body>` background differs from the master's keeps its own. A slide whose `<body>` computes a `--color-text` apart from the master's prints a `✗` line, since its bands keep the master's colors and may not read against its ground.
134
+
135
+ ## Speaker notes
136
+
137
+ `<aside class="notes">` becomes the slide's notes and is never drawn. Each paragraph or list item becomes its own line, and markup is dropped.
138
+
139
+ ## Charts
140
+
141
+ A `<figure data-chart="...">` holding a `<table>` becomes a native, editable chart where the figure was laid out. The table is not drawn as well.
142
+
143
+ ```html
144
+ <figure data-chart="bar" data-labels>
145
+ <figcaption>Revenue by region</figcaption>
146
+ <table>
147
+ <tr>
148
+ <th>Region</th>
149
+ <th>Q1</th>
150
+ <th>Q2</th>
151
+ </tr>
152
+ <tr>
153
+ <td>North</td>
154
+ <td>12</td>
155
+ <td>16</td>
156
+ </tr>
157
+ <tr>
158
+ <td>South</td>
159
+ <td></td>
160
+ <td>11</td>
161
+ </tr>
162
+ </table>
163
+ </figure>
164
+ ```
165
+
166
+ - `data-chart` takes `bar`, `line`, `area`, `pie`, or `doughnut`.
167
+ - The header row names the series and the first column names the categories.
168
+ - An empty cell is a gap, not a zero. A comma is a thousands separator only where it groups in threes, as in `12,345.5`, so a comma-decimal `3,5` refuses.
169
+ - `data-labels` shows each value, and a `<figcaption>` becomes the chart title.
170
+ - Series take the accent first, then every other declared `--color-*` role in sheet order except the background and surface, repeating past the last.
171
+
172
+ A refused chart, effect, or face is left out and the rest of the deck is written. Each prints a `✗` line, and the exit depends on the case:
173
+
174
+ | Case | Exit |
175
+ | ----------------------------------------------------------------------- | ---- |
176
+ | malformed or unknown `deck.json` field | 1 |
177
+ | `mark` path missing | 1 |
178
+ | `fonts` path missing or outside the folder | 1 |
179
+ | face that cannot embed | 0 |
180
+ | unknown transition or entrance name | 0 |
181
+ | entrance on an element that draws nothing | 0 |
182
+ | chart cell that is not a number, or `data-chart` off-list | 0 |
183
+ | token role not declared, or a slide body setting its own `--color-text` | 0 |
@@ -9,12 +9,17 @@ description: What branch naming is for, the gaps it closes, and where the family
9
9
 
10
10
  Without this skill, a branch name is invented once per session. The type prefix drifts from what the history already carries, the name runs past the width a pull request list renders, and a rename lands on a branch that already exists on the remote, which detaches every open pull request pointing at it.
11
11
 
12
+ A session standing on `main` has no route either. The `git.md` rule sends every git operation through a `git-*` skill, and the only one for branches stopped on `Cannot rename a protected branch`, so the first act of any feature, creating its branch, had no skill to go through.
13
+
12
14
  ## Must
13
15
 
14
16
  - Read the branch format reference before generating, so the type vocabulary and the length limit come from one source
15
- - Derive the name from the commits ahead of main when the invocation carries no description
17
+ - Create the branch on `main` or `master` and rename it anywhere else, so one skill covers both ends of a branch's start
18
+ - Derive the name from the commits ahead of main when a rename carries no description
19
+ - Take a created name from `canon tasks plan-branch` when the invocation names a plan, so the skill and the dispatch gate hold one derivation
16
20
  - Resolve whether the branch exists on the remote before proposing a rename, since the hazard is invisible from local state
17
- - Show the name and its length against the limit before renaming
21
+ - Read the remote itself with `git ls-remote` before creating, since `origin/<name>` sees only the last fetch
22
+ - Show the name and its length against the limit before renaming or creating
18
23
  - Execute immediately after the preview. The tool permission dialog is the confirmation gate.
19
24
 
20
25
  ## Must not
@@ -22,17 +27,23 @@ Without this skill, a branch name is invented once per session. The type prefix
22
27
  - Rename `main` or `master`
23
28
  - Rename when the current name already conforms. The skill is chained, so a conforming name has to pass through rather than churn the branch.
24
29
  - Rename a branch that exists on the remote. Local rename plus force push is not the same operation and it breaks the pull request.
30
+ - Create a branch from anywhere but `main` or `master`, or create one whose name exists locally or on the remote
31
+ - Invent a name when the plan verb refuses, since a name the skill calls non-conforming strands the rename at ship
32
+ - Fetch or stash while creating
25
33
  - Emit anything after the result line
26
34
 
27
35
  ## Guards
28
36
 
29
- - On a protected branch: stop and say the branch is protected
30
- - Name already conforms: stop with a success marker, not a failure, since a chained caller continues past it
31
- - No commits and no description: stop and name both missing inputs
32
- - Branch exists on the remote: stop and route to the surface that preserves the pull request link
37
+ - Create with no plan and no name: stop and name both missing inputs
38
+ - Create with a name breaking the branch standard: stop and name the cap
39
+ - Create with a name existing locally or on the remote: stop and say which
40
+ - Rename name already conforms: stop with a success marker, not a failure, since a chained caller continues past it
41
+ - Rename with no commits and no description: stop and name both missing inputs
42
+ - Rename of a branch existing on the remote: stop and route to the surface that preserves the pull request link
33
43
 
34
44
  ## Out of scope
35
45
 
36
46
  - The commit message, which `git-commit` owns
37
47
  - Pushing and opening the pull request, which `git-pr` owns. That skill reads a conforming name as a precondition and refuses without one, so this skill runs first.
38
48
  - Splitting a branch whose commits span concerns, which `git-split` owns
49
+ - Entering a worktree, which `session-worktree` owns and which names its own branch
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: git-branch
3
- description: Generates and validates conventional branch names. Use for any branch naming or renaming, such as when asked to "name this branch".
3
+ description: Generates and validates conventional branch names, creating the branch from main or master and renaming it anywhere else. Use for any branch naming, creating, or renaming, such as when asked to "name this branch" or "create the branch for this feature".
4
4
  metadata:
5
5
  family: ship
6
6
  ---
@@ -13,7 +13,64 @@ Before generating a branch name, read:
13
13
 
14
14
  Follow it exactly.
15
15
 
16
- ## Context
16
+ Run `git branch --show-current` to pick the mode. On `main` or `master` the skill creates a branch, and anywhere else it renames the current one.
17
+
18
+ ## Create mode
19
+
20
+ ### Name
21
+
22
+ Take the name from the invocation. An argument ending in `.md` or resolving under `.canon/plans/` is a plan, and anything else is a branch name, since a name always carries a `<type>/` segment.
23
+
24
+ - For a plan, run `canon tasks plan-branch <plan> --json` and take the record's `branch`. The verb fixes the type at `feat`, so a plan never yields another type.
25
+ - When the verb refuses or the record carries `conforms: false`, stop and relay its reason. Never invent a name in its place.
26
+ - For a name, check it against the whole Format section of `branch.md`, including the description word bound and the no-repeated-type rule, as the rename guard does.
27
+
28
+ ### Context
29
+
30
+ Run these in parallel once the name is known:
31
+
32
+ - `git show-ref --verify --quiet "refs/heads/<name>" && echo "LOCAL_EXISTS" || echo "LOCAL_FREE"`
33
+ - `git ls-remote --exit-code --heads origin "<name>" 2>/dev/null; echo "REMOTE_EXIT=$?"`
34
+
35
+ Exit 0 on the remote read is a collision. Exit 2, or a repository with no `origin`, is no collision. The check reads the remote itself, never `origin/<name>`, which sees only the last fetch. The skill does not fetch.
36
+
37
+ ### Guards
38
+
39
+ Each guard stops before any ref moves.
40
+
41
+ - If the invocation carries no plan and no name, stop: `❌ No plan or branch name to create from.`
42
+ - If the name breaks `branch.md`, stop and name the cap it broke: `❌ <name> breaks the branch standard: <cap>.`
43
+ - If the name exists locally, stop: `❌ Branch <name> already exists locally.`
44
+ - If the name exists on the remote, stop: `❌ Branch <name> already exists on origin.`
45
+
46
+ ### Preview
47
+
48
+ Output the preview first.
49
+
50
+ - **Current:** <main_or_master>
51
+ - **Creating:** <name>
52
+ - **Length:** <count>/50
53
+ - **Source:** <plan | name>
54
+
55
+ After outputting the preview, execute the final command immediately. Claude Code's tool permission dialog is the confirmation gate. Do not wait for user input.
56
+
57
+ ### Final command
58
+
59
+ The branch starts at the current tip, and uncommitted changes carry onto it. The skill stashes nothing.
60
+
61
+ ```bash
62
+ git switch -c <name>
63
+ ```
64
+
65
+ ### After execution
66
+
67
+ Respond with exactly one line and add no other text:
68
+
69
+ `✅ Created: <name>`
70
+
71
+ ## Rename mode
72
+
73
+ ### Context
17
74
 
18
75
  Run these commands in parallel to gather git context:
19
76
 
@@ -21,14 +78,13 @@ Run these commands in parallel to gather git context:
21
78
  - `git rev-parse --verify "origin/$(git branch --show-current)" 2>/dev/null && echo "EXISTS" || echo "LOCAL_ONLY"`
22
79
  - `git log main..HEAD --oneline 2>/dev/null || echo "NO_COMMITS"`
23
80
 
24
- ## Guards
81
+ ### Guards
82
+
83
+ A conforming name passes through, so a chained caller continues past it.
25
84
 
26
- - If already on main or master, stop: `❌ Cannot rename a protected branch.`
27
85
  - If branch name already follows conventions, stop: `✅ Branch name already follows conventions.`
28
86
  - If no commits and no args provided, stop: `❌ No commits or description to derive a branch name from.`
29
87
 
30
- ## Response format
31
-
32
88
  ### Preview
33
89
 
34
90
  Output the preview first.
@@ -55,7 +111,7 @@ Only output if LOCAL_ONLY:
55
111
  git branch -m <current> <suggested>
56
112
  ```
57
113
 
58
- ## After execution
114
+ ### After execution
59
115
 
60
116
  Respond with exactly one line and add no other text:
61
117