@erclx/canon 5.20.0 → 5.22.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 (98) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/auto-ship/SKILL.md +1 -1
  3. package/claude/skills/auto-ship/references/draft-mark.md +8 -6
  4. package/claude/skills/auto-ship/references/verb-absent.md +1 -1
  5. package/claude/skills/docs-sync/REQUIREMENT.md +1 -1
  6. package/claude/skills/document-health/REQUIREMENT.md +1 -1
  7. package/claude/skills/draft-doc/REQUIREMENT.md +45 -0
  8. package/claude/skills/draft-doc/SKILL.md +77 -0
  9. package/claude/skills/draft-doc/references/context.md +31 -0
  10. package/claude/skills/draft-doc/references/docs.md +33 -0
  11. package/claude/skills/draft-doc/references/readme.md +58 -0
  12. package/claude/skills/draft-doc/references/wiki.md +53 -0
  13. package/claude/skills/draft-doc/references/wireframes.md +48 -0
  14. package/claude/skills/draft-figure/REQUIREMENT.md +1 -1
  15. package/claude/skills/draft-figure/SKILL.md +1 -1
  16. package/claude/skills/draft-ready/REQUIREMENT.md +2 -1
  17. package/claude/skills/draft-ready/SKILL.md +6 -4
  18. package/claude/skills/git-pr/SKILL.md +5 -46
  19. package/claude/skills/git-pr/references/final-command.md +75 -0
  20. package/claude/skills/git-pr/references/labels.md +2 -2
  21. package/claude/skills/markdown-propose/REQUIREMENT.md +3 -3
  22. package/claude/skills/markdown-propose/SKILL.md +1 -1
  23. package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +76 -0
  24. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +11 -1
  25. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +5 -1
  26. package/claude/skills/search-craft/REQUIREMENT.md +4 -4
  27. package/claude/skills/search-craft/SKILL.md +1 -1
  28. package/docs/agents/commands.md +1 -0
  29. package/docs/agents/drift-surfaces.md +20 -9
  30. package/docs/agents/index.md +2 -1
  31. package/docs/agents/install-and-sync.md +3 -3
  32. package/docs/agents/markdown-audit.md +1 -3
  33. package/docs/agents/migrate.md +1 -1
  34. package/docs/agents/rule-citations.md +1 -1
  35. package/docs/agents/slides.md +164 -0
  36. package/docs/agents/tasks-validate.md +1 -1
  37. package/docs/agents/tasks.md +2 -2
  38. package/docs/target/migrations.md +4 -2
  39. package/docs/target/sync.md +1 -1
  40. package/docs/workflow/ai-workflow.md +1 -1
  41. package/docs/workflow/skill-map.md +25 -31
  42. package/governance/rules/canon/604-scratch.md +0 -1
  43. package/governance/rules/claude/575-hooks.md +1 -2
  44. package/governance/rules/claude/576-settings.md +1 -2
  45. package/governance/rules/lib/330-testing-py.md +1 -1
  46. package/governance/rules/lib/336-testing-php.md +1 -1
  47. package/governance/rules/standards/architecture.md +0 -1
  48. package/governance/rules/standards/context.md +0 -1
  49. package/governance/rules/standards/design.md +0 -1
  50. package/governance/rules/standards/diagrams.md +0 -3
  51. package/governance/rules/standards/requirements.md +0 -1
  52. package/governance/rules/standards/wireframes.md +0 -1
  53. package/package.json +1 -1
  54. package/scripts/manage-sandbox.sh +7 -8
  55. package/src/claude/cases/authoring.ts +5 -5
  56. package/src/claude/cases/workflow.ts +4 -20
  57. package/src/commands/slides.ts +23 -3
  58. package/src/commands/sync.ts +10 -0
  59. package/src/commands/tasks.ts +46 -2
  60. package/src/gov/citations.ts +1 -2
  61. package/src/migrate/skill-names.ts +12 -7
  62. package/src/slides/convert/chart.ts +165 -0
  63. package/src/slides/convert/deck.ts +244 -0
  64. package/src/slides/convert/export.ts +281 -23
  65. package/src/slides/convert/master.ts +195 -0
  66. package/src/slides/convert/shapes.ts +44 -4
  67. package/src/slides/convert/walk.ts +229 -2
  68. package/src/slides/package/fonts.ts +297 -0
  69. package/src/slides/package/motion.ts +316 -0
  70. package/src/slides/package/test-face.ts +113 -0
  71. package/src/sync/check.ts +9 -0
  72. package/src/sync/layout.ts +27 -1
  73. package/src/tasks/record.ts +11 -1
  74. package/src/tasks/validate.ts +14 -0
  75. package/standards/architecture.md +0 -1
  76. package/standards/context.md +0 -1
  77. package/standards/design.md +0 -1
  78. package/standards/diagrams.md +0 -3
  79. package/standards/requirements.md +0 -1
  80. package/standards/rule.md +1 -0
  81. package/standards/tasks.md +1 -1
  82. package/standards/wireframes.md +0 -1
  83. package/tooling/astro/configs/card-src/pages/og-card.astro +2 -2
  84. package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +8 -3
  85. package/claude/skills/docs-fold/REQUIREMENT.md +0 -25
  86. package/claude/skills/docs-fold/SKILL.md +0 -9
  87. package/claude/skills/draft-context/REQUIREMENT.md +0 -37
  88. package/claude/skills/draft-context/SKILL.md +0 -61
  89. package/claude/skills/draft-docs/REQUIREMENT.md +0 -35
  90. package/claude/skills/draft-docs/SKILL.md +0 -59
  91. package/claude/skills/draft-readme/REQUIREMENT.md +0 -38
  92. package/claude/skills/draft-readme/SKILL.md +0 -73
  93. package/claude/skills/draft-wiki/REQUIREMENT.md +0 -42
  94. package/claude/skills/draft-wiki/SKILL.md +0 -83
  95. package/claude/skills/draft-wireframes/REQUIREMENT.md +0 -38
  96. package/claude/skills/draft-wireframes/SKILL.md +0 -69
  97. package/claude/skills/standards-audit/REQUIREMENT.md +0 -33
  98. package/claude/skills/standards-audit/SKILL.md +0 -99
@@ -0,0 +1,164 @@
1
+ ---
2
+ title: Slides
3
+ description: What a folder of HTML slides declares for the whole deck in deck.json, the faces it embeds, what each slide declares on its own body, transitions and entrances, the master built from the project's tokens, speaker notes, charts, and what each refusal reports
4
+ ---
5
+
6
+ # Slides
7
+
8
+ `canon slides render --source <folder>` turns a folder of `.html` slides into an editable PowerPoint deck. Each slide describes its own shapes. What belongs to the whole deck, being the bands, the numbers, and the mark, sits in one file beside them, and the master that carries it takes the project's own colors and face.
9
+
10
+ ```bash
11
+ canon slides render --source slides/q3-review
12
+ ```
13
+
14
+ ## The deck file
15
+
16
+ `deck.json` beside the slides is optional. Without it the deck gets its defaults.
17
+
18
+ | Field | Default | What it sets |
19
+ | -------------- | ------------------------------ | ------------------------------------------------- |
20
+ | `title` | the folder name | the file's title and the default footer text |
21
+ | `header` | off | a band at the top, with `left`, `center`, `right` |
22
+ | `footer` | on, with the title on the left | a band at the bottom, with the same three slots |
23
+ | `slideNumbers` | `true` | the number at the right of the footer |
24
+ | `mark` | none | an image path, relative to the folder, top right |
25
+ | `fonts` | none | faces to embed in the file, described below |
26
+
27
+ ```json
28
+ {
29
+ "title": "Q3 review",
30
+ "header": { "left": "Acme" },
31
+ "footer": { "center": "Internal" },
32
+ "mark": "assets/mark.png",
33
+ "fonts": [
34
+ { "family": "Inter", "path": "fonts/Inter-Regular.ttf" },
35
+ { "family": "Inter", "weight": 700, "path": "fonts/Inter-Bold.ttf" }
36
+ ]
37
+ }
38
+ ```
39
+
40
+ 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.
41
+
42
+ Slide numbers belong to the footer and hold its right slot. They show only where the footer does, and `footer.right` needs `slideNumbers` set to `false`.
43
+
44
+ Prefer a raster mark. An SVG mark lands as a vector, and its fallback for a viewer that cannot draw SVG comes out broken.
45
+
46
+ ### Embedded faces
47
+
48
+ Each entry in `fonts` names one TrueType or OpenType file inside the deck folder, and the deck carries it so it renders in its own face on a machine without the font installed.
49
+
50
+ - `family` is required and should match the family the slides set, since that is the name PowerPoint looks the face up by.
51
+ - `weight` defaults to `400`. PowerPoint holds four faces a family, so `600` and above fills the bold slot and anything lighter fills regular.
52
+ - `style` is `normal` or `italic`, defaulting to `normal`.
53
+ - `path` is relative to the folder and has to stay inside it.
54
+
55
+ A face is refused, and the deck still written without it, when its license forbids embedding or allows a bitmap only, when it is a variable font, when it is a `woff` or `woff2` file, or when a second face lands in a slot one already fills. Supply static instances, one per weight, in place of a variable face. For `woff2`, supply the TrueType or OpenType file it was compressed from, which most font downloads ship beside it.
56
+
57
+ ## What each slide declares
58
+
59
+ A slide sets its own place in the deck through attributes on `<body>`.
60
+
61
+ | Attribute | Effect |
62
+ | --------------------------------------- | --------------------------------------------- |
63
+ | `data-section="Results"` | opens a section the slides after it stay in |
64
+ | `data-hidden` | hides the slide in the show |
65
+ | `data-header="off"` | drops the header on this slide |
66
+ | `data-footer="off"` | drops the footer and its number on this slide |
67
+ | `data-footer-left`, `-center`, `-right` | replaces that footer slot on this slide alone |
68
+ | `data-header-left`, `-center`, `-right` | replaces that header slot on this slide alone |
69
+
70
+ Once any slide opens a section, slides ahead of the first one sit in a section named after the deck, since PowerPoint wants every slide in one. A title the deck returns to after another section opens a new section numbered `Intro (2)`, so each section stays one run of slides.
71
+
72
+ A slide dropping one band keeps the other from the master. A slide replacing a slot draws its band as its own text, so editing that band once in PowerPoint's master view reaches every slide except the ones that replaced it.
73
+
74
+ ## Motion
75
+
76
+ A slide declares how it arrives on `<body>`, and an element declares how it comes in on its own tag.
77
+
78
+ | Attribute | Takes | Effect |
79
+ | -------------------------- | ------------------------------- | -------------------------------------- |
80
+ | `data-transition` | `fade`, `push`, `wipe`, `cover` | the transition into this slide |
81
+ | `data-transition-duration` | `500`, `500ms`, or `0.5s` | rounded to 0.5, 0.75, or 1 second |
82
+ | `data-enter` | `fade`, `fly`, `wipe`, `zoom` | an entrance played on a click |
83
+ | `data-enter-order` | a number | the click it plays on, lowest first |
84
+ | `data-enter-duration` | `500`, `500ms`, or `0.5s` | the entrance length, 500 ms by default |
85
+
86
+ ```html
87
+ <body data-transition="fade">
88
+ <h1>Results</h1>
89
+ <div class="card" data-enter="fly" data-enter-order="1">…</div>
90
+ <div class="card" data-enter="fly" data-enter-order="2">…</div>
91
+ </body>
92
+ ```
93
+
94
+ Each entrance is its own click. Entrances without an order play after the ordered ones, in page order. An entrance brings in every shape drawn from its element and the elements inside it together, so a card's box and its text arrive on one click, and an element drawn as a picture brings in that picture. An element inside it with its own `data-enter` waits for its own click. Fly comes in from the bottom and wipe from the left.
95
+
96
+ A transition length rounds to the nearest of the three speeds PowerPoint's file format names, since a length in milliseconds needs an extension the deck does not write.
97
+
98
+ ## The master
99
+
100
+ The master takes its colors from the `<html>` element of the first slide as Chromium laid it out with the project's token stylesheet injected, so a theme switched on one slide's `<body>`, such as a dark cover, stays on that slide:
101
+
102
+ - `--color-background`: the slide ground
103
+ - `--color-text`: the header text
104
+ - `--color-muted`: the footer text
105
+ - `--color-accent`: the slide number
106
+ - the `<body>` font family, first name with a ` Variable` suffix dropped: the theme face
107
+
108
+ 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.
109
+
110
+ 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 one `✗` line naming it, since its bands keep the master's colors and may not read against its ground.
111
+
112
+ ## Speaker notes
113
+
114
+ `<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.
115
+
116
+ ## Charts
117
+
118
+ A `<figure data-chart="...">` holding a `<table>` becomes a native, editable chart placed where the figure was laid out. The table stays the page's accessible source and is not drawn as well.
119
+
120
+ ```html
121
+ <figure data-chart="bar" data-labels>
122
+ <figcaption>Revenue by region</figcaption>
123
+ <table>
124
+ <tr>
125
+ <th>Region</th>
126
+ <th>Q1</th>
127
+ <th>Q2</th>
128
+ </tr>
129
+ <tr>
130
+ <td>North</td>
131
+ <td>12</td>
132
+ <td>16</td>
133
+ </tr>
134
+ <tr>
135
+ <td>South</td>
136
+ <td></td>
137
+ <td>11</td>
138
+ </tr>
139
+ </table>
140
+ </figure>
141
+ ```
142
+
143
+ - `data-chart` takes `bar`, `line`, `area`, `pie`, or `doughnut`.
144
+ - The header row names the series and the first column names the categories.
145
+ - 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 rather than reading as 35.
146
+ - `data-labels` shows each value. A `<figcaption>` becomes the chart title.
147
+ - Series take the accent first, then every other declared `--color-*` role in sheet order except the background and surface, repeating past the last.
148
+
149
+ ## Refusals
150
+
151
+ | Case | Report | Exit |
152
+ | -------------------------------------- | ---------------------------------------------- | ---- |
153
+ | `deck.json` field malformed or unknown | `✗` naming the file and the field | 1 |
154
+ | `mark` path missing | `✗` naming the resolved path | 1 |
155
+ | `fonts` path missing or outside folder | `✗` naming the field and the path | 1 |
156
+ | face not embeddable | `✗` naming the path and the reason | 0 |
157
+ | unknown transition or entrance name | `✗` naming the slide, the element, the effect | 0 |
158
+ | entrance on an element that draws none | `✗` naming the slide and the element | 0 |
159
+ | chart cell not a number | `✗` naming the slide, the figure, and the cell | 0 |
160
+ | `data-chart` outside the five types | `✗` naming the value | 0 |
161
+ | token role not declared | `✗` naming the role | 0 |
162
+ | slide body sets its own `--color-text` | `✗` naming the slides | 0 |
163
+
164
+ A refused chart, effect, or face is left out and the rest of the deck is written.
@@ -61,7 +61,7 @@ The blocker check re-takes a measurement the board records once and never repeat
61
61
  }
62
62
  ```
63
63
 
64
- A closed outcome is not the fact the dependency half needs. The ship chain marks outcomes as its first step and opens the pull request several steps later, so a check reading the checkbox reports the row settled while the branch is still in review. A live task therefore settles the row only once it closed every outcome and carries a `Pull request:` line the trunk holds. One that names no pull request, and one whose number no trunk ref could answer for, land in the untested array below rather than being settled or left silent.
64
+ A closed outcome is not the fact the dependency half needs. The ship chain marks outcomes as its first step and opens the pull request several steps later, so a check reading the checkbox reports the row settled while the branch is still in review. A live task therefore settles the row only once it closed every outcome and carries a `Pull request:` line the trunk holds. One that carries a `Pending branch:` line, one that names no pull request, and one whose number no trunk ref could answer for, land in the untested array below rather than being settled or left silent.
65
65
 
66
66
  The trunk is read as the clone already holds it, `origin/main` first and local `main` behind it, and no run fetches. A validate runs several times a sweep and a fetch per run is a cost this command does not carry, so a clone behind its remote under-reports rather than claiming work landed.
67
67
 
@@ -64,7 +64,7 @@ Run from a branch other than `main` or `master`, the verb also drops that branch
64
64
 
65
65
  Exit codes: `0` recorded, `1` refused. The `reason` field carries `no-board`, `no-match`, or `ambiguous`. `git-pr` skips silently on those three, because each is a case where a guessed write would archive the wrong task once the branch merges.
66
66
 
67
- A malformed argument refuses as `bad-input` instead, which sits outside that set on purpose. `git-pr` derives the number by slicing whatever `gh pr create` printed, so a non-numeric value is reachable, and folding it into the swallowed set would lose the number with nothing reporting it.
67
+ A malformed argument refuses as `bad-input` instead, which sits outside that set on purpose. `git-pr` derives the number by slicing the address the create call returned, so a non-numeric value is reachable, and folding it into the swallowed set would lose the number with nothing reporting it.
68
68
 
69
69
  ## Outcome
70
70
 
@@ -84,7 +84,7 @@ canon tasks outcome --plan worktree-scratch-routing --close 2 --json
84
84
 
85
85
  An outcome already closed comes back under `alreadyClosed` rather than refusing, so a rerun against the same positions changes nothing. A position past the end of the list refuses as `out-of-range`, since a caller counting wrong should hear about it rather than mark a neighbor.
86
86
 
87
- A close that ticks at least one box from a branch other than `main` or `master` names that branch on a `Pending branch:` line, read from `git branch --show-current` in the working directory. The line holds the task against `canon tasks archive --pull-request` until `canon tasks pull-request` records that branch's own number. A detached HEAD, a trunk checkout, or a failed git read writes no line and refuses nothing. The `pending` field lists the branches on the line after the write.
87
+ A close that ticks at least one box from a branch other than `main` or `master` names that branch on a `Pending branch:` line, read from `git branch --show-current` in the working directory. The line holds the task against `canon tasks archive --pull-request` until `canon tasks pull-request` records that branch's own number. A detached HEAD, a trunk checkout, or a failed git read writes no line and refuses nothing. The line is also skipped when `gh pr list --head <branch> --state all` lists a number already on the task's `Pull request:` line, since that branch has recorded itself and a marker would outlive it, as in a review-address pass. A failed, offline, or unreadable `gh` lookup counts as no listing and writes the line. The `pending` field lists the branches on the line after the write.
88
88
 
89
89
  Positions skip fenced blocks. A checkbox inside a sample a task displays is not an outcome the task claims, and counting one would shift every position after it. `canon tasks archive` reads the list through the same walker, so the two verbs cannot disagree about which checkboxes are outcomes.
90
90
 
@@ -60,9 +60,11 @@ The first line reports the plan and the second applies it, relocating each rule
60
60
 
61
61
  ## Rename the skill citations, once
62
62
 
63
- Twenty-five plugin skills dropped their `claude-` prefix for two-word names, so `canon:claude-docs` answers as `canon:context-fold` and `canon:claude-tasks` as `canon:task-board`. A project that installed governance or tooling before that release holds files naming the old ones, and the plugin answers to none of them. A later release renamed `docs-fold` to `context-fold`, since the skill folds a session into the context entries and the task board rather than into `docs/`, and the same sweep carries a project across that rename too. <!-- canon-keep-retired -->
63
+ Twenty-five plugin skills dropped their `claude-` prefix for two-word names, so `canon:claude-docs` answers as `canon:context-fold` and `canon:claude-tasks` as `canon:task-board`. A project that installed governance or tooling before that release holds files naming the old ones, and the plugin answers to none of them. A later release renamed `docs-fold` to `context-fold`, since the skill folds a session into the context entries and the task board rather than into `docs/`, and the same sweep carries a project across that rename too. The pointer that answered under the old name no longer ships, so a citation the sweep has not reached names nothing. <!-- canon-keep-retired -->
64
64
 
65
- Two of those files run rather than sit there. `.husky/post-merge` runs the `canon hooks post-merge` verb, so a stale copy of the hook calls a binary that may lack it, and `.claude/hooks/pr-create-log.sh` hands a session a message naming a skill, so a stale copy tells someone to invoke something that no longer exists. A rule under `.claude/rules/canon/core/` names skills too, though a rule is read rather than run.
65
+ The same sweep carries the merge of the five per-kind document drafters into `draft-doc`, so a citation of any one of them lands on `canon:draft-doc`.
66
+
67
+ Two of those files run rather than sit there. `.husky/post-merge` runs the `canon hooks post-merge` verb, so a stale copy of the hook calls a binary that may lack it, and `.claude/hooks/pr-create-log.sh` hands a session a message naming a skill, so a stale copy tells someone to invoke something that no longer exists. A copy older than `git-pr`'s move to REST also matches only `gh pr create`, so it stays silent on every pull request `git-pr` opens. A rule under `.claude/rules/canon/core/` names skills too, though a rule is read rather than run.
66
68
 
67
69
  Resync what the toolkit owns, then sweep what the project wrote:
68
70
 
@@ -28,7 +28,7 @@ Each domain holds its own toolkit commit, so syncing governance today does not m
28
28
 
29
29
  A project that has never synced under a toolkit new enough to write a stamp falls back to the toolkit's own git history. Installed content matching any version that history published proves the file untouched, so it reports `stale` naming the commit it came from, and content matching no published version stays `drifted`. That fallback needs the toolkit as a git checkout. Installed from the registry it ships source without history, and the report says attribution was unavailable rather than reading every file as a local edit.
30
30
 
31
- Further causes sit outside the per-domain scan, each naming something that walk cannot see. A seed the project edited is reported under `seeds` and reconciled with `canon:seed-sync`, since no sync command touches a seed. A file a newer seed folder replaced is reported under `superseded`, such as `.claude/TASKS.md` against the `.canon/tasks/` that now ships, and nothing moves it because the content is the project's own. A domain sitting at the root layout with nothing under `.claude/` is reported under `unmigrated`, and no command moves it either. The project moves the content itself.
31
+ Further causes sit outside the per-domain scan, each naming something that walk cannot see. A seed the project edited is reported under `seeds` and reconciled with `canon:seed-sync`, since no sync command touches a seed. A file a newer seed folder replaced is reported under `superseded`, such as `.claude/TASKS.md` against the `.canon/tasks/` that now ships, and nothing moves it because the content is the project's own. A domain sitting at the root layout with nothing under `.claude/` is reported under `unmigrated`, and no command moves it either. The project moves the content itself. A surface still at its `.claude/` spelling with nothing under `canon/`, such as `.claude/REQUIREMENTS.md`, is reported under `unmovedSurfaces`, since rules glob the `canon/` spelling alone, and `canon migrate surface-roots` moves it. <!-- canon-keep-surface-root -->
32
32
 
33
33
  `unmigrated` currently names no domain, since standards and snippets are the two the toolkit ever installed at the project root, standards closed its install channel, and snippets retired outright. A target still holding a root `standards/` folder from an older toolkit is carrying its own authoring surface now, not an unfinished install, and nothing proposes moving it. A leftover `.claude/snippets/` reports under `reverse` below instead.
34
34
 
@@ -97,7 +97,7 @@ Review findings split by origin before severity is read. One the branch inherite
97
97
 
98
98
  Review is skipped when the diff is prose that only informs: every changed file matches `*.md` or `*.txt`, and none sits under a behavior path. Behavior paths cover skills and rules in both the authoring and the installed spelling, so the list matches whether a repository authors those surfaces or consumed them from the toolkit. Standards, `internal/`, and `tooling/` carry the authoring spelling alone, since none of the three reaches a session through a `.claude/` copy, and root `CLAUDE.md` is named as a file because a path prefix reaches nothing sitting in no folder.
99
99
 
100
- Markdown under one states what an agent does, so a branch touching it reaches review while `docs/` and `wiki/` still skip and stay gated by `docs-sync`, `standards-audit`, and pre-push hooks.
100
+ Markdown under one states what an agent does, so a branch touching it reaches review while `docs/` and `wiki/` still skip and stay gated by `docs-sync`, `canon markdown audit`, and pre-push hooks.
101
101
 
102
102
  An empty changed-file list stops the chain rather than counting as prose-only. The filename test passes vacuously on an empty set, which routed a branch past review instead of through it.
103
103
 
@@ -61,7 +61,6 @@ This page is the corpus the coverage claim is measured against: every name `cano
61
61
  | Skill | When to use |
62
62
  | ------------------------ | --------------------------------------------------------------------------------------- |
63
63
  | `canon:review-branch` | On the local branch diff, before anything is pushed |
64
- | `canon:standards-audit` | When changed markdown has to answer to the authoring standards |
65
64
  | `canon:document-health` | When the documents themselves have to answer for length, placement, and staleness |
66
65
  | `canon:markdown-propose` | When a markdown claim needs rewriting and the change should wait for an answer per file |
67
66
  | `canon:ux-audit` | To read UI source for missing states, edge cases, and inconsistencies |
@@ -70,18 +69,17 @@ This page is the corpus the coverage claim is measured against: every name `cano
70
69
 
71
70
  ## Ship it
72
71
 
73
- | Skill | When to use |
74
- | ---------------------- | ------------------------------------------------------------------------------------------------- |
75
- | `canon:git-ship` | To run the whole post-feature chain from the verify gate through open PR |
76
- | `canon:memory-capture` | First skill in that chain, to route what the session learned to the surface owning it |
77
- | `canon:context-fold` | When decisions diverged from the plan, or a shipped task needs its outcomes marked |
78
- | `canon:docs-fold` | Never on purpose. A pointer at the old name that names `context-fold` <!-- canon-keep-retired --> |
79
- | `canon:docs-sync` | When a change since main left `README.md` or `docs/` stale |
80
- | `canon:git-stage` | When the staged set spans several concerns and wants one commit each |
81
- | `canon:git-commit` | When the staged set is one concern, or was staged hunk by hand |
82
- | `canon:git-branch` | When a branch name needs generating or renaming to conventional form |
83
- | `canon:git-pr` | When a pull request needs a title and body written from the diff |
84
- | `canon:memory-review` | When the pen has grown, to propose where each entry belongs |
72
+ | Skill | When to use |
73
+ | ---------------------- | ------------------------------------------------------------------------------------- |
74
+ | `canon:git-ship` | To run the whole post-feature chain from the verify gate through open PR |
75
+ | `canon:memory-capture` | First skill in that chain, to route what the session learned to the surface owning it |
76
+ | `canon:context-fold` | When decisions diverged from the plan, or a shipped task needs its outcomes marked |
77
+ | `canon:docs-sync` | When a change since main left `README.md` or `docs/` stale |
78
+ | `canon:git-stage` | When the staged set spans several concerns and wants one commit each |
79
+ | `canon:git-commit` | When the staged set is one concern, or was staged hunk by hand |
80
+ | `canon:git-branch` | When a branch name needs generating or renaming to conventional form |
81
+ | `canon:git-pr` | When a pull request needs a title and body written from the diff |
82
+ | `canon:memory-review` | When the pen has grown, to propose where each entry belongs |
85
83
 
86
84
  ## After the pull request opens
87
85
 
@@ -120,24 +118,20 @@ This page is the corpus the coverage claim is measured against: every name `cano
120
118
 
121
119
  ## Generate an artifact on demand
122
120
 
123
- | Skill | When to use |
124
- | ------------------------- | ----------------------------------------------------------------------------------------------------------------- |
125
- | `canon:create-rule` | For a project-specific governance rule the toolkit does not ship |
126
- | `canon:create-skill` | For a new `SKILL.md` |
127
- | `canon:create-standard` | For a new authoring convention |
128
- | `canon:draft-docs` | For a brand-new `docs/*.md` page, drafted against `standards/docs.md` |
129
- | `canon:draft-ready` | For finished files a worker should copy, written as a ready folder with its overview, thin plan, and task |
130
- | `canon:draft-context` | For a brand-new `canon/context/<domain>.md` entry, drafted against `standards/context.md` |
131
- | `canon:draft-wireframes` | For a brand-new `canon/wireframes/<surface>.md` file, drafted against `standards/wireframes.md` |
132
- | `canon:draft-wiki` | For a brand-new wiki reference page on a subject owned outside the project, drafted against `standards/wiki.md` |
133
- | `canon:draft-figure` | For a hand-drawn figure inside an existing doc, drafted against `standards/figures.md` in Mermaid or freehand SVG |
134
- | `canon:draft-readme` | For a project's `README.md`, drafted against `standards/readme.md` |
135
- | `canon:bash-cli-script` | For a small non-interactive shell wrapper, stopping at 100 lines in favor of TypeScript on Bun |
136
- | `canon:ci-workflow` | For a GitHub Actions workflow file |
137
- | `canon:draft-slides` | For a deck, drafted as `.claude/SLIDES.md` and rendered to PowerPoint |
138
- | `canon:draft-screencast` | For a recording script with beats and defaults already seeded |
139
- | `canon:record-screencast` | For recording a screencast draft, then composing the take into a finished mp4 when the draft asks for a wrap |
140
- | `canon:draft-identity` | For a project's logo mark and its social card, drafted through `draft-and-pick`'s pick loop |
121
+ | Skill | When to use |
122
+ | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
123
+ | `canon:create-rule` | For a project-specific governance rule the toolkit does not ship |
124
+ | `canon:create-skill` | For a new `SKILL.md` |
125
+ | `canon:create-standard` | For a new authoring convention |
126
+ | `canon:draft-doc` | For a brand-new `docs/` page, context entry, wireframe surface, wiki reference page, or `README.md`, drafted against the standard for its kind |
127
+ | `canon:draft-ready` | For finished files a worker should copy, written as a ready folder with its overview, thin plan, and task |
128
+ | `canon:draft-figure` | For a hand-drawn figure inside an existing doc, drafted against `standards/figures.md` in Mermaid or freehand SVG |
129
+ | `canon:bash-cli-script` | For a small non-interactive shell wrapper, stopping at 100 lines in favor of TypeScript on Bun |
130
+ | `canon:ci-workflow` | For a GitHub Actions workflow file |
131
+ | `canon:draft-slides` | For a deck, drafted as `.claude/SLIDES.md` and rendered to PowerPoint |
132
+ | `canon:draft-screencast` | For a recording script with beats and defaults already seeded |
133
+ | `canon:record-screencast` | For recording a screencast draft, then composing the take into a finished mp4 when the draft asks for a wrap |
134
+ | `canon:draft-identity` | For a project's logo mark and its social card, drafted through `draft-and-pick`'s pick loop |
141
135
 
142
136
  ## Answer a question at any point
143
137
 
@@ -7,4 +7,3 @@ description: Write temporary files to the scratch folder structure
7
7
  ## Temporary files
8
8
 
9
9
  - Write temporary files to `.canon/tmp/<slug>/<file>.md` at the current worktree's root, a nested folder with a kebab-slug tied to the topic rather than a flat `<slug>-<file>.md`. The slug must not take one of the five reserved names `runs`, `hooks`, `handoff`, `pr`, and `render`. A skill that resolves a write at the main root instead states so in its own body.
10
- - Write to `.claude/.tmp/<slug>/` instead in a project that carries no `.canon/` root, and `canon migrate records` moves it to the first spelling. <!-- canon-keep-record-root -->
@@ -1,8 +1,7 @@
1
1
  ---
2
- description: State the hook stdin guard and the silencing rule for .claude/hooks scripts and their seeded copies
2
+ description: State the hook stdin guard and the silencing rule for .claude/hooks scripts
3
3
  paths:
4
4
  - '.claude/hooks/**/*.sh'
5
- - 'tooling/claude/seeds/.claude/hooks/**/*.sh'
6
5
  ---
7
6
 
8
7
  # Hook standards
@@ -1,8 +1,7 @@
1
1
  ---
2
- description: State the autoContinueAtUsageLimit inversion and the autoCompactWindow cap for .claude/settings.json and its seeded copy
2
+ description: State the autoContinueAtUsageLimit inversion and the autoCompactWindow cap for .claude/settings.json
3
3
  paths:
4
4
  - '.claude/settings.json'
5
- - 'tooling/claude/seeds/.claude/settings.json'
6
5
  ---
7
6
 
8
7
  # Settings standards
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: Enforce pytest fixtures, parametrize, and async patterns for Python tests
3
3
  paths:
4
- - 'tests/**/*.py'
4
+ - '**/tests/**/*.py'
5
5
  - '**/test_*.py'
6
6
  - '**/*_test.py'
7
7
  ---
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: Enforce PHPUnit test classes, attributes, and strict assertions for PHP tests
3
3
  paths:
4
- - 'tests/**/*.php'
4
+ - '**/tests/**/*.php'
5
5
  - '**/*Test.php'
6
6
  ---
7
7
 
@@ -3,7 +3,6 @@
3
3
  description: Shape and content rules for canon/ARCHITECTURE.md
4
4
  paths:
5
5
  - 'canon/ARCHITECTURE.md'
6
- - '.claude/ARCHITECTURE.md'
7
6
  ---
8
7
 
9
8
  # Architecture standards
@@ -3,7 +3,6 @@
3
3
  description: Shape and content rules for canon/context/<domain>.md entries
4
4
  paths:
5
5
  - 'canon/context/**'
6
- - '.claude/context/**'
7
6
  ---
8
7
 
9
8
  # Context entry standards
@@ -3,7 +3,6 @@
3
3
  description: Shape and content rules for canon/DESIGN.md
4
4
  paths:
5
5
  - 'canon/DESIGN.md'
6
- - '.claude/DESIGN.md'
7
6
  ---
8
7
 
9
8
  # Design standards
@@ -3,7 +3,6 @@
3
3
  description: Shape and content rules for .canon/diagrams/<kind>.md files
4
4
  paths:
5
5
  - '.canon/diagrams/**'
6
- - '.claude/DIAGRAMS.md'
7
6
  ---
8
7
 
9
8
  # Diagram standards
@@ -13,5 +12,3 @@ paths:
13
12
  - Follow the diagrams standard for shape and content rules for .canon/diagrams/<kind>.md files. It is the single source. Read it with `canon standards diagrams`.
14
13
  - Layout, budgets, accessibility, labels, and render verification inside the fence are a separate topic. The mermaid standard routes them.
15
14
  - A diagram entry carries structure and flow, not implementation. Read the standard before adding or revising a kind.
16
- - Write a new diagram to `.canon/diagrams/<kind>.md`, never to `.claude/DIAGRAMS.md`
17
- - Convert a `.claude/DIAGRAMS.md` left by an older install into per-kind entries before editing it
@@ -3,7 +3,6 @@
3
3
  description: Shape and content rules for canon/REQUIREMENTS.md
4
4
  paths:
5
5
  - 'canon/REQUIREMENTS.md'
6
- - '.claude/REQUIREMENTS.md'
7
6
  ---
8
7
 
9
8
  # Requirements standards
@@ -3,7 +3,6 @@
3
3
  description: Shape and content rules for canon/wireframes/<surface>.md files
4
4
  paths:
5
5
  - 'canon/wireframes/**'
6
- - '.claude/wireframes/**'
7
6
  ---
8
7
 
9
8
  # Wireframe standards
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "5.20.0",
4
+ "version": "5.22.0",
5
5
  "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -190,10 +190,10 @@ provision_sandbox() {
190
190
  (cd "$SANDBOX" && configure_sandbox_git_credentials)
191
191
  }
192
192
 
193
- # Runs the real installer rather than copying the source tree. The two copies
194
- # these replaced each reimplemented an installer's selection rules, so a change
195
- # to what install produces left the sandbox provisioning the old shape and no
196
- # scenario could observe the difference.
193
+ # Runs the real installer rather than copying the source tree. The copies these
194
+ # replaced, for gov rules and for seeds, each reimplemented an installer's
195
+ # selection rules, so a change to what install produces left the sandbox
196
+ # provisioning the old shape and no scenario could observe the difference.
197
197
  run_sandbox_install() {
198
198
  local label="$1"
199
199
  shift
@@ -223,10 +223,9 @@ inject_gov_rules() {
223
223
  }
224
224
 
225
225
  inject_seeds() {
226
- local seeds_source="$PROJECT_ROOT/tooling/claude/seeds"
227
- if [ -d "$seeds_source" ]; then
228
- cp -r "$seeds_source/." "$SANDBOX/"
229
- fi
226
+ [ ! -d "$PROJECT_ROOT/tooling/claude/seeds" ] && return
227
+
228
+ run_sandbox_install "seeds" claude init "$SANDBOX"
230
229
  }
231
230
 
232
231
  commit_environment_setup() {
@@ -31,7 +31,7 @@ export const AUTHORING_CASES: readonly SkillCase[] = [
31
31
  {
32
32
  prompt:
33
33
  'Write a brand-new docs page for the capture command, nothing under docs/ covers it yet.',
34
- expect: 'draft-docs',
34
+ expect: 'draft-doc',
35
35
  },
36
36
  {
37
37
  prompt:
@@ -40,22 +40,22 @@ export const AUTHORING_CASES: readonly SkillCase[] = [
40
40
  },
41
41
  {
42
42
  prompt: 'This project has no README.md at all, write one from scratch.',
43
- expect: 'draft-readme',
43
+ expect: 'draft-doc',
44
44
  },
45
45
  {
46
46
  prompt:
47
47
  'Write a context entry for the payments domain, there is no canon/context page for it yet.',
48
- expect: 'draft-context',
48
+ expect: 'draft-doc',
49
49
  },
50
50
  {
51
51
  prompt:
52
52
  'Draft a wireframe for the settings panel, nothing under canon/wireframes covers that surface yet.',
53
- expect: 'draft-wireframes',
53
+ expect: 'draft-doc',
54
54
  },
55
55
  {
56
56
  prompt:
57
57
  'Write a wiki reference page for Claude Code output styles, no page covers that subject yet.',
58
- expect: 'draft-wiki',
58
+ expect: 'draft-doc',
59
59
  },
60
60
  {
61
61
  prompt: 'Say what that dense answer actually means in plain terms.',
@@ -38,12 +38,6 @@ export const WORKFLOW_CASES: readonly SkillCase[] = [
38
38
  'Fold what we decided this session into the context entries and mark off what shipped on the task board.',
39
39
  expect: 'context-fold',
40
40
  },
41
- {
42
- prompt:
43
- 'I typed the old fold skill name and it says it was renamed, where did it go?',
44
- // The pointer answers under its old name on purpose. canon-keep-retired
45
- expect: 'docs-fold',
46
- },
47
41
  {
48
42
  prompt:
49
43
  'Sketch out a plan for adding this new capability before we touch any code.',
@@ -143,16 +137,10 @@ export const WORKFLOW_CASES: readonly SkillCase[] = [
143
137
  prompt: 'Turn this topic into a slide deck I can render.',
144
138
  expect: 'draft-slides',
145
139
  },
146
- {
147
- prompt:
148
- 'Check whether the markdown I changed violates any authoring standards.',
149
- expect: 'standards-audit',
150
- },
151
- // Rot across documents nobody changed is what separates this from
152
- // `standards-audit`, which is diff-scoped and reports a stated rule broken on
153
- // a given line. It is also not `target-check`, which asks whether a document
154
- // exists and holds its sections rather than whether it still describes the
155
- // tree.
140
+ // Rot across documents nobody changed is what this reads, rather than a
141
+ // stated rule broken on a given line. It is also not `target-check`, which
142
+ // asks whether a document exists and holds its sections rather than whether
143
+ // it still describes the tree.
156
144
  {
157
145
  prompt:
158
146
  'Which of our docs have gone stale, grown too long, or ended up in the wrong file?',
@@ -230,10 +218,6 @@ export const WORKFLOW_CASES: readonly SkillCase[] = [
230
218
  "Go through what's still unanswered in the intake folder and decide.",
231
219
  expect: 'plan-intake-answer',
232
220
  },
233
- {
234
- prompt: 'Check this markdown against the house style rules.',
235
- expect: 'standards-audit',
236
- },
237
221
  {
238
222
  prompt: 'Find the rough, unfinished-feeling spots in this interface.',
239
223
  expect: 'ux-audit',
@@ -119,9 +119,11 @@ export function register(program: Command): void {
119
119
 
120
120
  /**
121
121
  * The converter loads on demand, so no other command resolves pptxgenjs,
122
- * jszip, or the token module at startup. A fallback or a refused link reports
123
- * one line each and keeps the exit at 0, as an unrecognized layout does on the
124
- * markdown path, since the deck is written either way.
122
+ * jszip, or the token module at startup. A fallback, a refused link, chart, or
123
+ * effect, or a token the master fell back on reports one line each and keeps
124
+ * the exit at 0, as an unrecognized layout does on the markdown path, since the
125
+ * deck is written either way. A malformed `deck.json` refuses before any slide is laid
126
+ * out and exits 1.
125
127
  */
126
128
  async function renderHtmlFolder(
127
129
  sourceDir: string,
@@ -156,6 +158,24 @@ async function renderHtmlFolder(
156
158
  `${GREY}│${NC} ${RED}✗${NC} slide ${slide} ${selector} link "${href}" left out: ${reason}\n`,
157
159
  )
158
160
  }
161
+ for (const { slide, selector, message } of result.refusedCharts) {
162
+ process.stderr.write(
163
+ `${GREY}│${NC} ${RED}✗${NC} slide ${slide} ${selector} chart left out: ${message}\n`,
164
+ )
165
+ }
166
+ for (const { slide, message } of result.refusedMotion) {
167
+ process.stderr.write(
168
+ `${GREY}│${NC} ${RED}✗${NC} slide ${slide} motion left out: ${message}\n`,
169
+ )
170
+ }
171
+ for (const { path, message } of result.refusedFonts) {
172
+ process.stderr.write(
173
+ `${GREY}│${NC} ${RED}✗${NC} font ${path} not embedded: ${message}\n`,
174
+ )
175
+ }
176
+ for (const notice of result.notices) {
177
+ process.stderr.write(`${GREY}│${NC} ${RED}✗${NC} ${notice}\n`)
178
+ }
159
179
  if (result.mirrorPath) {
160
180
  process.stderr.write(
161
181
  `${GREY}│${NC} ${GREEN}✓${NC} mirrored to ${result.mirrorPath}\n`,
@@ -168,6 +168,16 @@ function renderCheck(report: CheckReport): void {
168
168
  logInfo('Move the content yourself. No sync command touches these.')
169
169
  }
170
170
 
171
+ if (report.unmovedSurfaces.length > 0) {
172
+ logStep('Surfaces at the old .claude/ root')
173
+ for (const entry of report.unmovedSurfaces) {
174
+ logWarn(`${entry.rel} (moves to ${entry.movesTo})`)
175
+ }
176
+ logInfo(
177
+ 'Rules glob the canon/ spelling only. Run `canon migrate surface-roots` to move these.',
178
+ )
179
+ }
180
+
171
181
  renderSeeds(report)
172
182
 
173
183
  if (report.superseded.length > 0) {