@erclx/canon 4.108.0 → 4.110.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.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  <div align="center">
2
2
 
3
3
  <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="assets/brand/mark-accent.svg">
5
- <img src="assets/brand/mark-accent-light.svg" alt="canon" width="56" height="56">
4
+ <source media="(prefers-color-scheme: dark)" srcset="assets/brand/mark-accent.svg">
5
+ <img src="assets/brand/mark-accent-light.svg" alt="canon" width="56" height="56">
6
6
  </picture>
7
7
 
8
8
  # canon
@@ -17,12 +17,7 @@ canon is a CLI and Claude Code plugin that stops your AI conventions drifting ap
17
17
 
18
18
  </div>
19
19
 
20
- <picture>
21
- <source media="(prefers-color-scheme: dark)" srcset="assets/evidence/arrival.png">
22
- <img src="assets/evidence/arrival-light.png" alt="The first screen of the canon landing page, a project before and after one canon init, with the file counts each step lays down">
23
- </picture>
24
-
25
- The page is one real session the toolkit ran on itself, and every count on it is read from the repo when the page builds.
20
+ That page is one real session the toolkit ran on itself, and every count on it is read from this repository when the page builds.
26
21
 
27
22
  ## Why
28
23
 
@@ -46,8 +41,8 @@ claude plugin install canon@canon
46
41
  ```
47
42
 
48
43
  <picture>
49
- <source media="(prefers-color-scheme: dark)" srcset="assets/evidence/install.png">
50
- <img src="assets/evidence/install-light.png" alt="Adding the canon marketplace and installing the plugin in Claude Code">
44
+ <source media="(prefers-color-scheme: dark)" srcset="assets/evidence/install.png">
45
+ <img src="assets/evidence/install-light.png" alt="Adding the canon marketplace and installing the plugin in Claude Code">
51
46
  </picture>
52
47
 
53
48
  The skills land as `/canon:<name>`. If your session was already open, run `/reload-plugins` to pick them up.
@@ -73,35 +68,21 @@ Each domain has a canonical source in this repo and a thin install or sync CLI o
73
68
  | Tooling stacks | Golden configs, seeds, and a reference per framework | Laid down by `canon init`, reconciled by `canon tooling sync` |
74
69
  | Design system | Ships a `DESIGN.md` token format, a skill that drafts one from an existing project or from scratch, and a render command | `canon design render` |
75
70
  | Slides | A `SLIDES.md` source format with a layout catalog | `canon slides render` writes PowerPoint |
71
+ | Teach | Lessons, sources, and a glossary in one workspace, rendered to a small site | `canon teach` |
76
72
  | Transcripts | A YouTube transcript with metadata frontmatter | `canon transcripts <url>` writes it into any repo |
77
73
  | Sandbox | Scenario scaffolds that provision an isolated project state for verifying each domain flow | `canon sandbox` |
78
74
 
79
- <picture>
80
- <source media="(prefers-color-scheme: dark)" srcset="assets/evidence/catalog.png">
81
- <img src="assets/evidence/catalog-light.png" alt="The canon catalog as a ledger of five rows, with the count each ships and a few real names: skills, rules, standards, commands, and stacks">
82
- </picture>
83
-
84
- Every count in that image is read from the catalogs when it is built, so they're what the repo ships today. A tooling stack lands as real files under version control, because a config is something your build reads and your project owns. A standard stays here and is opened by name, so there is no copy in your repo to drift from this one. Governance is the third shape. A rule with a path glob loads only when a matching path is edited, and a rule with none loads every session. Run `canon gov list` to see the glob beside each rule.
85
-
86
- Two of the outputs are documents you open rather than files you run. A `SLIDES.md` renders to a deck, and a learning workspace renders to a small site.
75
+ 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 standard stays here and is opened by name, so there is no copy in your repo to drift from this one.
87
76
 
88
- <picture>
89
- <source media="(prefers-color-scheme: dark)" srcset="examples/slides/evidence/showcase-dark-04.png">
90
- <img src="examples/slides/evidence/showcase-light-04.png" alt="A slide from the showcase deck rendered by canon slides render, a bulleted list under a heading on a warm background">
91
- </picture>
92
-
93
- <picture>
94
- <source media="(prefers-color-scheme: dark)" srcset="examples/teach/evidence/root-listing.png">
95
- <img src="examples/teach/evidence/root-listing-light.png" alt="The learning workspace listing rendered by canon teach, three workspaces with their lesson and term counts, one still a stub">
96
- </picture>
77
+ Governance is the third shape. A rule with a path glob loads only when a matching path is edited, and a rule with none loads every session. Run `canon gov list` to see the glob beside each rule.
97
78
 
98
79
  ## It runs on itself
99
80
 
100
81
  The workflow this toolkit ships is the workflow that built it. Several Claude Code sessions run at once, each in its own git worktree on its own branch, and each opens its own pull request.
101
82
 
102
83
  <picture>
103
- <source media="(prefers-color-scheme: dark)" srcset="demos/agent-view.gif">
104
- <img src="demos/agent-view-light.gif" alt="The canon landing page scrolling from the dispatch section to the branch graph, three workers and a planner on disjoint file sets with the pull request each opened">
84
+ <source media="(prefers-color-scheme: dark)" srcset="demos/agent-view.gif">
85
+ <img src="demos/agent-view-light.gif" alt="The canon landing page scrolling from the dispatch section to the branch graph, three workers and a planner on disjoint file sets with the pull request each opened">
105
86
  </picture>
106
87
 
107
88
  That recording is the landing page's own `dispatch` and `workers` sections, driven by `canon demo run` against a local build. The branch graph beside the pull request numbers is authored rather than read, because nothing on a build machine records which files four sessions held.
@@ -110,6 +91,8 @@ That recording is the landing page's own `dispatch` and `workers` sections, driv
110
91
 
111
92
  Scaffolding your first project? Start with target projects, then the AI workflow loop. Everything else answers questions that arrive later.
112
93
 
94
+ Working with an agent rather than reading this yourself? It loads `CLAUDE.md` at your project root first, then whichever rules under `.claude/rules/` match the paths it edits. Run `canon docs agents` for the command catalog with flags, exit codes, and JSON shapes.
95
+
113
96
  - [AI workflow](docs/workflow/ai-workflow.md): feature-development loop inside a toolkit-managed project
114
97
  - [Operating model](docs/workflow/operating-model.md): orchestrator, planner, and worker roles for building across parallel sessions
115
98
  - [Visual design workflow](docs/workflow/visual-design-workflow.md): tiered guide for design and wireframe authoring
@@ -165,7 +148,9 @@ git init
165
148
  canon init
166
149
  ```
167
150
 
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. Governance defaults to the `base` stack, so a bare init lands the coding and doc-authoring rules in `.claude/rules/`. Each rule names the standard it answers to and reads it with `canon standards <name>`, so no corpus is copied into your project. Pass `--stack <name>` for a framework stack, or `--skip governance` to leave rules out. A snippet resolves the same way, reached at its `@` reference through the plugin's live `claude/snippets` symlink rather than a copy. Run `canon tooling list --json` to see the catalog.
151
+ `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 coding and doc-authoring rules in `.claude/rules/`. Each rule names the standard it answers to and reads it with `canon standards <name>`, so no corpus is copied into your project.
152
+
153
+ Pass `--stack <name>` for a framework stack, or `--skip governance` to leave rules out. A snippet resolves the same way, reached at its `@` reference through the plugin's live `claude/snippets` symlink rather than a copy. Run `canon tooling list --json` to see the catalog.
169
154
 
170
155
  For the full journey from scaffold through adding a domain later to syncing upstream drift, see [target projects](docs/target-projects.md).
171
156
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "4.108.0",
4
+ "version": "4.110.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -1,9 +1,9 @@
1
1
  ---
2
- name: canon-feedback-file
3
- description: Why a toolkit defect is reported from session context alone, and what shipping through the CLI replaces
2
+ name: canon-feedback
3
+ description: Why a toolkit defect is reported from session context alone, and what opening an issue through the CLI replaces
4
4
  ---
5
5
 
6
- # Canon feedback file requirement
6
+ # Canon feedback requirement
7
7
 
8
8
  ## Gap
9
9
 
@@ -18,8 +18,9 @@ The last failure is the session's own effort. Asked to report, a session starts
18
18
  - Build the report from what the session already holds
19
19
  - Name the surface and its type, since the toolkit routes on that rather than on the symptom
20
20
  - Write the stated fallback for a field the session cannot fill, so an empty field reads as absent rather than as unreported
21
- - Ship through the CLI so the report lands without a copy-paste, and print the returned path on its own line
22
- - Offer the durable route for a report worth keeping across sessions and machines
21
+ - Open a GitHub issue on the toolkit repo through the CLI so the report lands where triage reads it, and print the returned URL on its own line
22
+ - Say which file and which reason when the issue call fails and the report falls back to a local folder, since a silent fallback leaves it where nothing reads it
23
+ - Hold a report the operator marks sensitive, since the issue is public
23
24
  - Fall back to printing the block when the CLI is absent, so the report still exists
24
25
 
25
26
  ## Must not
@@ -36,5 +37,5 @@ The last failure is the session's own effort. Asked to report, a session starts
36
37
 
37
38
  - Fixing the defect, which happens in the toolkit repository against its own source
38
39
  - Draining the queue this fills: `canon-feedback-triage`
39
- - Filing an issue against the current project: `git-issue`
40
+ - Filing an issue against the current project, which `git-issue` does and this skill cannot delegate to, since that skill targets the current repository
40
41
  - Complaints about tooling the toolkit does not own
@@ -1,11 +1,11 @@
1
1
  ---
2
- name: canon-feedback-file
3
- description: Format a paste-back report about something broken, missing, or off in canon and write it directly to the toolkit's `.canon/feedback/` folder via `canon feedback`. Use when asked to "send this to the toolkit", "report this to canon", "file toolkit feedback", or "give the toolkit feedback about X". Do NOT use for general complaints about other tooling, IDE issues, or in-project bugs that do not implicate canon surfaces.
2
+ name: canon-feedback
3
+ description: Format a report about something broken, missing, or off in canon and open it as a GitHub issue on the toolkit repo via `canon feedback --github`, where the toolkit's triage reads it. Use when asked to "send this to the toolkit", "report this to canon", "file toolkit feedback", or "give the toolkit feedback about X". Do NOT use for general complaints about other tooling, IDE issues, or in-project bugs that do not implicate canon surfaces.
4
4
  ---
5
5
 
6
- # Canon feedback file
6
+ # Canon feedback
7
7
 
8
- Format a `## Toolkit feedback` block from the current session, then ship it to the toolkit repo without manual copy-paste.
8
+ Format a `## Toolkit feedback` block from the current session, then open it as a GitHub issue labeled `feedback` on the toolkit repo. `canon-feedback-triage` reads that queue.
9
9
 
10
10
  ## Guards
11
11
 
@@ -69,9 +69,9 @@ Detect whether `canon` is on PATH:
69
69
  command -v canon >/dev/null 2>&1
70
70
  ```
71
71
 
72
- Before either pipe below, run the scan in `${CLAUDE_SKILL_DIR}/../../standards/publish.md` against the composed block. `.canon/review/` is gitignored, so no hook reaches either write, and this scan is the only gate. The banned-character half runs on both branches, including the local write. The phase-label half applies only on the `--github` branch, where the block reaches a remote.
72
+ The destination is a public GitHub issue on the toolkit repo, so the report leaves the machine. Hold it and ask when the operator has marked it sensitive, and never send it on a guess.
73
73
 
74
- Local scratch only writes from a toolkit checkout, since `canon feedback` resolves the destination against the toolkit's own `.claude/` folder and a target project never carries one. Pipe the block to `canon feedback --github` first.
74
+ Before the pipe below, run the scan in `${CLAUDE_SKILL_DIR}/../../standards/publish.md` against the composed block, phase-label half included. Nothing else checks what reaches the remote, so this scan is the only gate.
75
75
 
76
76
  ```bash
77
77
  cat <<'EOF' | canon feedback --github
@@ -81,19 +81,9 @@ cat <<'EOF' | canon feedback --github
81
81
  EOF
82
82
  ```
83
83
 
84
- The CLI opens a GitHub issue on the toolkit repo and prints the issue URL. It needs `gh` authenticated, and falls back to local scratch with the reason on stderr when the call produces no URL and the session holds a toolkit checkout to fall back onto.
84
+ The CLI opens a GitHub issue labeled `feedback` on the toolkit repo and prints the issue URL, which this skill reports back on its own line. It needs `gh` authenticated. `git-issue` is not the route, since it files on the current repository and this report belongs on the toolkit's.
85
85
 
86
- From a toolkit checkout, plain `canon feedback` reaches the same destination without opening an issue:
87
-
88
- ```bash
89
- cat <<'EOF' | canon feedback
90
- ## Toolkit feedback
91
-
92
- ...
93
- EOF
94
- ```
95
-
96
- It writes to `.canon/feedback/feedback-<slug>-<ts>.md` and prints the absolute path on stdout. Report the printed path back to the user on its own line, in the form the project's instruction file sets under `## Output`.
86
+ From a toolkit checkout, a call that produces no URL falls back to `.canon/feedback/feedback-<slug>-<ts>.md` and prints the reason on stderr. Say so in the same reply, naming the file and the reason, since a silent fallback leaves the report where nothing reads it. A target project has no such folder, so a failed call there prints the block in chat.
97
87
 
98
88
  If `canon` is not on PATH, fall back: print the block in chat and tell the user `📋 Copy the block above into a toolkit-repo session.`
99
89
 
@@ -109,4 +99,4 @@ A `--github` run from a toolkit checkout whose GitHub call fails warns and write
109
99
  ## Notes
110
100
 
111
101
  - `canon feedback` resolves the toolkit root from the running `canon` binary's source location. If multiple toolkit clones exist on the machine, the first `canon` on PATH wins.
112
- - The destination `.canon/review/` is gitignored in the toolkit repo. Feedback lives as session scratch for the next toolkit-side triage, not as a durable archive.
102
+ - The local folder is a fallback for a failed issue call, and nothing reads it on a schedule. The issue is the durable record.
@@ -35,6 +35,6 @@ The queue also fails to drain even when the work ships. A fix merged with no lin
35
35
 
36
36
  ## Out of scope
37
37
 
38
- - Filing new feedback: `canon-feedback-file`
38
+ - Filing new feedback: `canon-feedback`
39
39
  - Writing the plan a plan-worthy issue needs: `plan-feature`
40
40
  - Triage of issues carrying any other label, which surface here by design only under the feedback label
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: canon-feedback-triage
3
- description: Triage open GitHub issues labeled `feedback` in the toolkit repo. List them, pick one, classify it as a direct fix or plan-worthy work, route to the right skill, and link the issue for close-out. Use when asked to "triage toolkit feedback", "work through the feedback issues", "process feedback issues", or "what feedback is open". Do NOT use to file new feedback (that is `canon-feedback-file`), or for general GitHub issue triage unrelated to toolkit feedback.
3
+ description: Triage open GitHub issues labeled `feedback` in the toolkit repo. List them, pick one, classify it as a direct fix or plan-worthy work, route to the right skill, and link the issue for close-out. Use when asked to "triage toolkit feedback", "work through the feedback issues", "process feedback issues", or "what feedback is open". Do NOT use to file new feedback (that is `canon-feedback`), or for general GitHub issue triage unrelated to toolkit feedback.
4
4
  ---
5
5
 
6
6
  # Canon feedback triage
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: draft-wiki
3
+ description: Why a brand-new wiki reference page needs both placement tests, a catalog collision check, and a sourced read before anything is written
4
+ ---
5
+
6
+ # Wiki draft requirement
7
+
8
+ ## Gap
9
+
10
+ Without this skill, a session asked for a wiki page writes one from training knowledge, which is the single thing the wiki standard forbids outright, and nothing in the resulting file shows that it happened. It also files the page by where the request came from rather than by who owns the subject, so material about the project itself lands in the wiki and material about a third-party tool acquires a folder the standard never sanctioned.
11
+
12
+ The standard can state all of this and still be read by nothing, which is the condition this skill was written into in the toolkit. A page written under that condition conforms by the author's memory rather than by a read, and it ships looking identical to one that was checked.
13
+
14
+ ## Must
15
+
16
+ - Read the wiki standard before drafting, since the placement tests and the sourcing rule are the two things a session gets wrong from memory
17
+ - Run both placement tests and refuse on either, since a page passing one and failing the other is still non-conforming
18
+ - Check the subject against the catalog by title and description, not by slug alone, since a subject can be covered under a name the slug does not guess
19
+ - Build the draft on a fetched read and stop when the fetch fails, since a page written from recall is indistinguishable from a checked one once it ships
20
+ - Confirm the path, the source, and the content with the user before writing, since the standard forbids writing a wiki file unasked
21
+ - Name the folder by resolving what the standard states rather than by carrying a path in the body, since the body ships to projects whose layout this repository does not set
22
+
23
+ ## Must not
24
+
25
+ - Widen the folder split to admit a subject the second placement test rejects. That is a change to the standard, argued there.
26
+ - Edit or refresh a page that already exists. A collision refuses toward editing the existing page rather than drafting a second one.
27
+ - Substitute recall for a failed fetch, even for a subject the session believes it knows.
28
+
29
+ ## Guards
30
+
31
+ - No subject given: stop and ask what subject the page should cover.
32
+ - The wiki root does not resolve: say so and ask before creating it, since a project holding no wiki has never taken the placement rule.
33
+ - The slug already resolves to a page: stop and point at editing that page.
34
+ - The catalog already covers the subject under a different name: stop the same way, checked against the titles and descriptions the Collision step already reads.
35
+
36
+ ## Out of scope
37
+
38
+ - Editing or refreshing a wiki page that already exists: a plain edit against the same standard
39
+ - Drafting a `docs/*.md` page: `draft-docs`
40
+ - Drafting a `canon/context/<domain>.md` entry: `draft-context`
41
+ - Voice, rhythm, and sentence construction in the drafted page: the `write-human` skill
42
+ - Punctuation, headings, and word choice in the drafted page: the markdown standard
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: draft-wiki
3
+ description: Drafts a brand-new wiki reference page for a subject owned outside this repository, against the wiki standard. Checks the two placement tests and the catalog for a collision, sources the subject from its owner rather than from training knowledge, confirms with the user, then writes. Use when asked to "write a wiki page for X", "add a wiki reference for X", "document X in the wiki", or "create a reference page for X" where no page covers the subject yet. Do NOT use to edit or refresh a page that already exists, which is a plain edit against the same standard. Do NOT use for a subject this repository owns, which is `draft-docs` or `draft-context`.
4
+ ---
5
+
6
+ # Wiki draft
7
+
8
+ Drafts one brand-new wiki reference page end to end: read the standard, settle placement, check the catalog for a collision, source the subject from its owner, confirm the draft with the user, then write.
9
+
10
+ Read these files in parallel:
11
+
12
+ - `${CLAUDE_SKILL_DIR}/../../standards/wiki.md`: the two placement tests, the frontmatter contract, the naming convention, the sourcing rule, and the page template
13
+ - `${CLAUDE_SKILL_DIR}/../../standards/markdown.md`: banned words, punctuation, and formatting for all generated text
14
+ - The `write-human` skill: voice, rhythm, and sentence construction for all generated text
15
+
16
+ The standard's `## Placement` section names the folder a page lands in and the vendor subfolder under it. Resolve both from the project root and call them `<wiki-root>` and `<vendor>` below, so the body states the rule once and the standard stays the one owner of the path.
17
+
18
+ ## Guards
19
+
20
+ - If no subject is given, stop: `❌ No subject given. Name the subject this page should cover.`
21
+ - If `<wiki-root>` does not resolve, say so and ask whether to create it before drafting. A project carrying no such folder has never taken the standard's placement rule, so creating it is a decision rather than a step.
22
+
23
+ ## Placement
24
+
25
+ The standard sets two tests and a page has to pass both. Run them before drafting anything, since a page failing either is non-conforming however well it is written.
26
+
27
+ - **Owned outside this repository.** A subject describing how this repository works fails. Refuse and route it: `❌ <subject> is owned by this project, so it belongs in the docs surface, a context entry, or a skill body. Use draft-docs or draft-context.`
28
+ - **Owned by the vendor the folder split covers.** The standard names one owner and one subfolder. A third-party tool or a vendor-neutral concept fails, however reference-like it reads. Refuse and route it: `❌ <subject> is outside the wiki's folder split. Route it to the docs surface or a skill body rather than adding a second folder for it.`
29
+
30
+ Do not widen the split to admit a subject. Adding a sibling folder is a change to the standard, argued there, rather than a placement this skill takes on its own.
31
+
32
+ ## Collision
33
+
34
+ - Derive the page slug per the standard's `## Naming` section and check whether `<wiki-root>/<vendor>/<slug>.md` already exists. If it does, stop: `❌ <path> already covers this subject. Edit that page against the standard instead of drafting a second one.`
35
+ - Read `<wiki-root>/<vendor>/index.md` and check every title and description it lists against the subject. The slug check above only catches an exact filename collision, and a subject already covered under a different name still resolves here at no extra cost, since this read already runs.
36
+ - Stop the same way on a match: `❌ <path> already covers this subject under a different name. Edit that page instead.`
37
+
38
+ ## Source
39
+
40
+ The standard forbids working from training knowledge, so the draft is built on a read rather than on recall.
41
+
42
+ - Fetch current information through the `claude-code-guide` agent for any subject that agent covers. Its answer is the source the page is written from.
43
+ - Reach a subject the agent does not cover through the owner's own published documentation.
44
+ - Say the fetch failed and stop rather than substituting recall. A page sourced from memory is the failure the rule exists to prevent, and it ships looking identical to one that was checked.
45
+ - Close the intro with the `Source:` sentence the standard requires, linking the canonical page where one exists and naming the owner alone where the subject has no single URL.
46
+ - Stamp the read date on a source revised without notice, as `read <date>`. A claim traced to an undated read cannot be checked against what was actually read.
47
+
48
+ ## Draft
49
+
50
+ - Draft `title` and `description` frontmatter against the standard's contract, then the intro paragraph closing on `Source:`, then the reference sections.
51
+ - Draft against the standard's template and keep its ordering.
52
+ - Write what the fetch returned rather than what the subject is assumed to do.
53
+ - Answer one question per section and stop. A reader arrives to settle something specific rather than to read the page through.
54
+ - Mark a claim the fetch left unsettled rather than smoothing over it. An unmarked gap reads as a checked fact.
55
+
56
+ ## Confirm
57
+
58
+ - Show the resolved path, the source the page was built from, and the full drafted content before writing.
59
+ - Confirm all three with the user. This skill waits for that answer rather than treating the tool permission dialog as the gate, since the standard states outright that a wiki file is not written unasked.
60
+
61
+ ## Write
62
+
63
+ - Write the file at the confirmed path.
64
+ - Run `canon markdown audit <path>`.
65
+ - Run `canon indexes regen <wiki-root>/<vendor>`, naming the folder the page landed in rather than the root above it. The verb rewrites the `index.md` sitting in the folder it is given and does not walk down, so regenerating at the root leaves the vendor catalog untouched and the new page missing from the one the Collision step reads. A page absent there is invisible to the next run, which then drafts a second page on the same subject.
66
+
67
+ ## Response format
68
+
69
+ ### Preview
70
+
71
+ **Subject:** `<subject>`
72
+ **Placement:** `<path>`
73
+ **Source:** `<owner, and the read it came from>`
74
+
75
+ ```markdown
76
+ <drafted frontmatter and body>
77
+ ```
78
+
79
+ ### After confirmation
80
+
81
+ ```plaintext
82
+ ✅ Drafted: <path>
83
+ ```
@@ -33,6 +33,6 @@ Without this skill, an issue takes the shape of whatever the session was saying.
33
33
 
34
34
  ## Out of scope
35
35
 
36
- - Reporting a toolkit defect from a target project, which `canon-feedback-file` owns. That path writes into the toolkit rather than the current repository.
36
+ - Reporting a toolkit defect from a target project, which `canon-feedback` owns. That path opens an issue on the toolkit repository rather than the current one.
37
37
  - Triaging issues already filed, which `canon-feedback-triage` owns
38
38
  - Opening a pull request, which `git-pr` owns
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: git-issue
3
- description: Format a bug or task from the current session into a GitHub issue and file it on the current repo via `gh issue create`. Use when asked to "file an issue", "open an issue", "log this bug", "raise an issue", or "track this as an issue". Do NOT use to report a toolkit defect from a target project (that is `canon-feedback-file`), or to open a pull request (that is `git-pr`).
3
+ description: Format a bug or task from the current session into a GitHub issue and file it on the current repo via `gh issue create`. Use when asked to "file an issue", "open an issue", "log this bug", "raise an issue", or "track this as an issue". Do NOT use to report a toolkit defect from a target project (that is `canon-feedback`), or to open a pull request (that is `git-pr`).
4
4
  ---
5
5
 
6
6
  # Git issue
@@ -31,7 +31,7 @@ Run `git diff --cached --name-only 2>/dev/null` to check for staged files. If ou
31
31
  6. Invoke `canon:git-stage` to group staged changes and commit by concern
32
32
  7. Invoke `canon:git-branch` to rename branch to match conventional format
33
33
  8. Invoke `canon:git-pr` to push branch and open pull request
34
- 9. After the PR opens, watch CI. Poll `canon pr checks <number> --json` until the record's `state` leaves `pending`, branching on that field rather than on the exit, and fall back to `gh pr checks <number>` when no record comes back at all, which is a target whose CLI predates the verb. On `passing`, continue. On `failing`, stop the sequence and report the failing check with its URL. Do not auto-fix. This step may output on failure, the one exception to the no-text-between-steps rule.
34
+ 9. After the PR opens, watch CI. Poll `canon pr checks <number> --json` until the record's `state` leaves `pending` or the record carries `conflicted: true`, branching on those fields rather than on the exit, and fall back to `gh pr checks <number>` when no record comes back at all, which is a target whose CLI predates the verb. On `passing`, continue. On `failing`, stop the sequence and report the failing check with its URL. On `conflicted`, stop the sequence and report that the branch conflicts with its base, since no run will start for it and polling on never ends. A rebase is the repair. Do not auto-fix. This step may output on failure, the one exception to the no-text-between-steps rule.
35
35
 
36
36
  A caller wrapping this sequence may act between step 8 and step 9, which is the one gap the order leaves open, since the pull request exists there and nothing has read its checks yet. `auto-ship` marks the pull request draft in it. Nothing else may go there, and a caller that needs a step anywhere else in the sequence is asking for a change to this body rather than for a place to stand.
37
37
 
@@ -30,7 +30,7 @@ Also read the CI check status so the fixes cover failing checks, not only review
30
30
  canon pr checks <number> --json
31
31
  ```
32
32
 
33
- Read the verdict off the record's `state` rather than off the exit. Treat `failing` as a finding to resolve alongside the review comments. A `pending` covers a tip whose runs have yet to conclude and a tip carrying no run at all, which the record separates on `matched`, and neither is a green to continue on. Fall back to `gh pr checks <number>` when no record comes back at all, which is a target whose CLI predates the verb.
33
+ Read the verdict off the record's `state` rather than off the exit. Treat `failing` as a finding to resolve alongside the review comments. A `pending` covers a tip whose runs have yet to conclude and a tip carrying no run at all, which the record separates on `matched`, and neither is a green to continue on. A record carrying `conflicted: true` is a third case: the branch conflicts with its base, so no run will start and the wait never ends. Stop and report it, since a rebase is the repair. Fall back to `gh pr checks <number>` when no record comes back at all, which is a target whose CLI predates the verb.
34
34
 
35
35
  ## Step 2: address each finding
36
36
 
@@ -170,8 +170,9 @@ echo "${comment_url##*issuecomment-}" > .canon/tmp/pr/reply/reply-<number>.id
170
170
  ## Step 7: confirm resolution
171
171
 
172
172
  After the follow-up push, watch CI on the PR. Poll
173
- `canon pr checks <number> --json` until the record's `state` leaves `pending`,
174
- then read it. When every finding is addressed and the state is `passing`, append
173
+ `canon pr checks <number> --json` until the record's `state` leaves `pending`
174
+ or it carries `conflicted: true`, then read it. On `conflicted`, stop and report
175
+ that a rebase is the repair rather than polling on. When every finding is addressed and the state is `passing`, append
175
176
  the closing confirmation to the reply file Step 6 already posted, so the thread
176
177
  carries one terminal state rather than a second comment under no heading:
177
178
 
@@ -18,7 +18,7 @@ Placing a row without checking for another writer collides the same way. Two ses
18
18
  - Resolve the board at the main worktree root, since a linked worktree writing to `pwd` creates a second board nothing reads
19
19
  - Read the tasks standard before writing, rather than working the filename and frontmatter from memory
20
20
  - Require an origin at creation, because that is the only moment the invariant is enforceable
21
- - Read the phase label from `canon tasks next-label` rather than proposing one by hand
21
+ - Claim the phase label with `canon tasks next-label --claim` rather than reading or proposing one by hand
22
22
  - Check the roster for a live orchestrator before writing a row, and hand off rather than write when one is found
23
23
  - Confirm the work reached the default branch before archiving
24
24
  - Run the archive command and route on the reason it refuses, since each reason has one resolution
@@ -29,13 +29,13 @@ Accept work whose origin is the conversation itself only when the user says so e
29
29
 
30
30
  ### Step 2: allocate the phase label
31
31
 
32
- Run `canon tasks next-label --json` and take its `label` field. The verb reads the live board and its `archive/` sibling together, so the label it returns accounts for what the board alone no longer shows.
32
+ Run `canon tasks next-label --claim --json` and take its `label` field. The verb reads the live board and its `archive/` sibling together, so the label it returns accounts for what the board alone no longer shows, and `--claim` reserves it under `.canon/ordinal-locks/` so a second session filing at the same moment gets the next one. Branch on the record's `reason` rather than the exit: `label-contended` means re-run the claim, and a usage error from an unknown `--claim` means the installed binary predates the flag, so report the verb as unavailable rather than falling back to a bare read, which reopens the race.
33
33
 
34
34
  Do not derive the label from a version file. `${CLAUDE_SKILL_DIR}/../../standards/versioning.md` permits free renumbering, so the verb's two folders are the only surface that knows what a label currently means.
35
35
 
36
36
  ### Step 3: write the file
37
37
 
38
- Write `.canon/tasks/vXX.Y-<slug>.md` following the format in `${CLAUDE_SKILL_DIR}/../../standards/tasks.md`. Include a link line only when the file or folder it names exists. A link to a plan nobody has written yet is the broken pointer the archive rules exist to prevent.
38
+ Write `.canon/tasks/vXX.Y-<slug>.md` under the claimed label, reading the file first if a session ever stubbed it, following the format in `${CLAUDE_SKILL_DIR}/../../standards/tasks.md`. Include a link line only when the file or folder it names exists. A link to a plan nobody has written yet is the broken pointer the archive rules exist to prevent.
39
39
 
40
40
  Write `Plan:`, `Groundwork:`, and `Intake:` as markdown links relative to `.canon/tasks/`, as in `Plan: [feature-<slug>](../plans/feature-<slug>.md)`. Leave `Issue:` a bare `#NNN`. A task written in the older bare-path form still parses, so it costs the board a clickable line rather than an archive, but it leaves the board in two shapes for every reader after.
41
41
 
@@ -18,11 +18,11 @@ canon capture https://example.com --selector .window --out preview.png
18
18
 
19
19
  ## What this repository captures
20
20
 
21
- A capture set here spans two folders. `assets/captures/` holds five sources and the three templates they are written from, and `assets/evidence/` holds their images and the built-page capture a document points at with the stamp answering for each, under the segment `canon pr evidence` compares. One run over the source folder rebuilds every image, and it takes `--out assets/evidence` to put each PNG where its document points rather than beside the markup it rendered. None of the sources is edited by hand. `scripts/core/regen-hero.sh` writes each `.html` from the template beside it, filling one shared value map into all of them. A branch commits none of them for a moved count or token, since the refresh workflow regenerates them from `main` and opens one pull request, and `bun run check` runs the script with `--check`, which fills every template into a temp folder and fails when the regeneration cannot run.
21
+ A capture set here spans two folders. `assets/captures/` holds three sources and the two templates they are written from, and `assets/evidence/` holds their images and the built-page capture a document points at with the stamp answering for each, under the segment `canon pr evidence` compares. One run over the source folder rebuilds every image, and it takes `--out assets/evidence` to put each PNG where its document points rather than beside the markup it rendered. None of the sources is edited by hand. `scripts/core/regen-hero.sh` writes each `.html` from the template beside it, filling one shared value map into all of them. A branch commits none of them for a moved count or token, since the refresh workflow regenerates them from `main` and opens one pull request, and `bun run check` runs the script with `--check`, which fills every template into a temp folder and fails when the regeneration cannot run.
22
22
 
23
- Two of the three templates take catalog data, so a stack gaining a rule moves the frame on the next run. `install.html` is the exception, since it holds terminal text from a real run in its template rather than derived from a live catalog at build time. `assets/evidence/arrival.png` is the one image with no source here: it is the built page's `.fold` element captured in dark mode against a local `web/` preview, since `canon capture` takes no color scheme and the page follows the browser's default of light, so it is retaken by hand when the page's first screen changes.
23
+ The `hero` template takes catalog data, so a stack gaining a rule moves the frame on the next run. `install.html` is the exception, since it holds terminal text from a real run in its template rather than derived from a live catalog at build time. `assets/evidence/arrival.png` is the one image with no source here: it is the built page's `.fold` element captured in dark mode against a local `web/` preview, since `canon capture` takes no color scheme and the page follows the browser's default of light, so it is retaken by hand when the page's first screen changes.
24
24
 
25
- The README shows each still in two themes through `<picture>`, so a frame is a pair named `<name>` and `<name>-light`. `catalog` and `install` take the light half from the same template, which `regen-hero.sh` fills a second time with the plain color roles pointed at the light ones, and each half is its own capture set with its own stamp. `arrival-light.png` is the same `.fold` capture of the same address taken without forcing dark, so it is retaken by hand alongside `arrival.png`, and nothing catches one drifting from the other.
25
+ The README shows each still in two themes through `<picture>`, so a frame is a pair named `<name>` and `<name>-light`. `install` takes the light half from its template, which `regen-hero.sh` fills a second time with the plain color roles pointed at the light ones, and each half is its own capture set with its own stamp. `arrival-light.png` is the same `.fold` capture of the same address taken without forcing dark, so it is retaken by hand alongside `arrival.png`, and nothing catches one drifting from the other.
26
26
 
27
27
  A stamp for a URL source digests the address string rather than the page it served, so two themes of one address carry one `source-sha256` and only `image-sha256` tells the frames apart. That holds for `examples/teach/evidence/root-listing-light.png`, which is retaken by serving a copy of the fixture with `data-theme="light"` on its `<html>` element at the same address, since the page follows the dark default and the command takes no scheme.
28
28
 
@@ -5,7 +5,7 @@ description: Every project-level command and every domain subcommand, plus the s
5
5
 
6
6
  # Command catalog
7
7
 
8
- Full help: `canon <command> --help`. Bare `canon --help` lists every top-level command under five headings, Project, Domains, Author and render, Session and ship, and Repo reports, with descriptions cut at 80 columns. Behavior notes for the install and sync verbs live in `install-and-sync.md`.
8
+ Full help: `canon <command> --help`. Bare `canon --help` lists every top-level command under five headings, Project, Domains, Author and render, Session and ship, and Repo reports, with descriptions cut at 80 columns. Piped into another command, the block prints without its frame glyphs and carries the same text. Behavior notes for the install and sync verbs live in `install-and-sync.md`.
9
9
 
10
10
  ## Project-level
11
11
 
@@ -92,7 +92,7 @@ Full help: `canon <command> --help`. Bare `canon --help` lists every top-level c
92
92
  | `canon autoship classify` | Decide whether a changed set needs the review pass, naming the file and the test that decided it (`--json`) |
93
93
  | `canon pr key-changes` | Compare the files a pull request body's Key Changes names against its own diff, in both directions (`--body`, `--base`, `--json`) |
94
94
  | `canon pr head` | Compare the head a pull request object reports against the branch tip the remote carries, naming both shas (`--root`, `--json`) |
95
- | `canon pr checks` | Report the check runs belonging to the branch tip, reading pending for a tip carrying none rather than clean (`--root`, `--json`) |
95
+ | `canon pr checks` | Report the check runs belonging to the branch tip, reading pending for a tip carrying none rather than clean, and `conflicted` when the branch conflicts with its base (`--root`, `--json`) |
96
96
  | `canon pr review-state` | Report the commit and instant the last review pass covered, off the pass's own marker rather than the fields GitHub stamps at submission (`--root`, `--json`) |
97
97
  | `canon pr evidence` | Render a before-and-after comment comparing changed `evidence/` images against the merge base, editing its own prior comment in place rather than posting a second (`--root`, `--json`, `--preview`) |
98
98
  | `canon pr preview` | Dispatch the Pages deploy on a pull request's branch and report the preview address the run prints, refusing a deploy that passes no `--branch` (`--root`, `--json`, `--timeout`) |
@@ -74,6 +74,8 @@ Both empty cases report `pending` instead: a tip carrying no run yet, and a list
74
74
 
75
75
  The verb keeps the newest run per check name rather than rolling every matched run into the state. A workflow that fires twice on one commit used to leave its first, stale run beside the rerun, where a failing one could outvote a passing rerun. Grouping the matched set by name and keeping the highest `id` per group removes that stale run before the rollup runs. `collapsed` on the record counts what the fold discards, so a caller reading it against `matched` sees the dedupe directly, instead of reading a `matched` above the check count as the verb disagreeing with `gh pr checks`.
76
76
 
77
+ A conflicted branch produces no `pull_request` check runs rather than a failing one. GitHub builds no merge ref for it, so no `pull_request` workflow starts and a repository running only that trigger leaves the listing empty, which reads as `pending` and never leaves it. A workflow triggered by a push to the branch still runs on the tip, so `matched` rises above zero, `conflicted` stays false, and the wait ends on those runs rather than on the reading. `mergeStateStatus` is the field that separates the two, and the record carries `conflicted: true` and `mergeState` when it reads `DIRTY` with no run belonging to the tip. A `DIRTY` reading beside runs on the tip leaves the state as those runs say, since a conflict arising after they completed does not unsay them. `UNKNOWN` is transient while GitHub computes the merge state, right after a push, so it reads as not conflicted and costs one more poll. A binary predating the field keeps answering `pending`.
78
+
77
79
  A listing carrying even one foreign run reports `pending` whatever the matching half says. A set that describes another commit says nothing about this one, so answering off the rows that happen to match would put a verdict on a set already known to be incomplete.
78
80
 
79
81
  A count above the rows returned reports `pending` for the same reason. The query asks for 100 rows against the endpoint's default of 30, and a commit carrying more than that comes back short, so the verb reports the tip as unread rather than collapsing the page it happens to hold. `reported` against `matched` and `foreign` is where a reader sees which of the three shapes produced the answer.
@@ -7,25 +7,27 @@ description: Selecting a shipped task by stem or pull request, recording a numbe
7
7
 
8
8
  ## Next label
9
9
 
10
- `canon tasks next-label` reports the next unused phase label, reading `.canon/tasks/` and its `archive/` sibling together. A scan confined to the live board is blind to every label the archive already spent, which is what let two sessions hand out the same label within minutes of each other. It reports and never writes.
10
+ `canon tasks next-label` reports the next unused phase label, reading `.canon/tasks/` and its `archive/` sibling together. A scan confined to the live board is blind to every label the archive already spent, which is what let two sessions hand out the same label within minutes of each other. Bare, it reports and never writes. With `--claim` it reserves the label first.
11
11
 
12
12
  ```bash
13
13
  canon tasks next-label
14
14
  canon tasks next-label --json
15
+ canon tasks next-label --claim --json
15
16
  ```
16
17
 
17
- | Option | Effect |
18
- | --------------- | ------------------------------------------- |
19
- | `--json` | Emit a machine-readable record on stdout |
20
- | `--root <path>` | Board root, defaulting to the main worktree |
18
+ | Option | Effect |
19
+ | --------------- | ------------------------------------------------ |
20
+ | `--json` | Emit a machine-readable record on stdout |
21
+ | `--claim` | Reserve the label so a second claim gets another |
22
+ | `--root <path>` | Board root, defaulting to the main worktree |
21
23
 
22
- The record carries `label`, the next free `vXX.Y`, and `highest`, the label it was derived from. `highest` is absent when neither folder carries a label yet, and `label` reads `v01.0` in that case, matching the zero-padded-major shape every other label already takes. <!-- canon-allow-reference: illustrates the verb's answer for a board holding no label yet, not a citation of a real task -->
24
+ The record carries `label`, the next free `vXX.Y`, `claimed`, whether the label was reserved, and `highest`, the label it was derived from. `highest` is absent when neither folder carries a label yet, and `label` reads `v01.0` in that case, matching the zero-padded-major shape every other label already takes. <!-- canon-allow-reference: illustrates the verb's answer for a board holding no label yet, not a citation of a real task -->
23
25
 
24
- Exit codes: `0` derived, `1` refused with `no-board`.
26
+ Exit codes: `0` derived, `1` refused with `no-board`, `2` refused with `label-contended` after five lost reservations, on `--claim` only.
25
27
 
26
28
  The minor digit rolls from 9 to 0 on the next major rather than growing a second digit, which is the single-digit-minor shape every phase label already takes. `canon tasks archive` moves a task's file from the live folder into the archive without renumbering it, so the same label counts toward the maximum wherever it currently sits, and a label claimed by two different files folds into the same scan without a dedicated check.
27
29
 
28
- It reports rather than gates. Two sessions calling it in the same second can still take the same answer, since the board is gitignored files rather than a store with a lock, and `standards/versioning.md` permits free renumbering, so a collision costs a rename rather than anything worse.
30
+ A claim is an exclusive `mkdir` at `.canon/ordinal-locks/<label>`, and the scan counts every reservation beside the task files, since the file lands after the verb returns. Reservations never expire, so a claim that died leaves a gap, which `standards/versioning.md` permits. A bare read still reports without reserving, and it does not count reservations either, so it can return a label a claim already holds until that claim's task file lands. Two bare reads in the same second can also take the same answer.
29
31
 
30
32
  ```bash
31
33
  canon tasks next-label --json | jq -r '.label'
@@ -133,7 +133,7 @@ It reports three things. `lesson` is the numbered path the lesson takes, derived
133
133
 
134
134
  A lesson written against this record carries its chrome as four empty marker pairs rather than hand-composed markup: `canon:teach:style`, `canon:teach:header`, `canon:teach:footnav`, and `canon:teach:scripts`. `canon teach nav`, below, fills them from what the workspace holds on disk.
135
135
 
136
- `canon teach stylesheet <topic>` writes the one file every lesson in the workspace embeds, seeded from the design source so a workspace renders in the system every other surface does. It refuses to overwrite, reporting `written` as false where the workspace already carries one, and `--force` takes the seed back over it. Each workspace used to carry a hand-authored palette, which is how the course styling forked once per workspace, so a lesson adds its own rules under the seed and reaches a value through its custom property rather than restating the hex.
136
+ `canon teach stylesheet <topic>` writes the pair every lesson in the workspace renders through, seeded from the design source so a workspace renders in the system every other surface does. The two halves are written on different terms. `assets/base.css` holds the generated design layer and is rewritten on every run, which is the only way a change to the shared chrome reaches a workspace that already exists. `assets/course.css` is the half a workspace owns, so it refuses to overwrite, reporting `written` as false where the workspace already carries one, and `--force` takes the seed back over it. Each workspace used to carry a hand-authored palette, which is how the course styling forked once per workspace, so a lesson adds its own rules under the seed and reaches a value through its custom property rather than restating the hex.
137
137
 
138
138
  Each `quiz` entry carries `order`, the authored option indices in presentation order where index `0` is the correct answer, and `answer`, the one-based position that answer lands in. Both travel together because a caller deriving the position itself is a caller that can derive it wrongly.
139
139
 
@@ -172,10 +172,12 @@ canon teach nav regular-expressions --json
172
172
 
173
173
  With no topic it rewrites every workspace. The teach-root listing is always rewritten regardless, since it reports on every workspace and a scoped run leaving it stale would be a second kind of drift this verb exists to end.
174
174
 
175
- A lesson carries its chrome as four marker pairs the authoring skill writes empty: `canon:teach:style`, `canon:teach:header`, `canon:teach:footnav`, and `canon:teach:scripts`. This verb splices each one from the current workspace state, embedding the shared stylesheet, rebuilding the breadcrumb and jump menus, the prev/next footer nav, and the behavior scripts, and leaves the authored `<h1>`, lede, body, and quiz between the header and the footnav untouched.
175
+ A lesson carries its chrome as four marker pairs the authoring skill writes empty: `canon:teach:style`, `canon:teach:header`, `canon:teach:footnav`, and `canon:teach:scripts`. This verb splices each one from the current workspace state, embedding the shared stylesheet and the script that settles the course panel before the page paints, rebuilding the course sidebar, the breadcrumb and jump menus, the prev/next footer nav, and the behavior scripts, and leaves the authored `<h1>`, lede, body, and quiz between the header and the footnav untouched.
176
176
 
177
177
  A workspace carrying no `course.css` gets its stylesheet pair seeded first, so the contents page it links is never unstyled, and an existing `course.css` is never overwritten. The embedded copy drops the seed's `@import` line, which resolved against the lesson's own folder and never loaded anything.
178
178
 
179
+ That seeding fires on the absent file alone, so a workspace already holding a `course.css` has neither half rewritten here and its lessons go on embedding the sheet they were built against. A change to the shared chrome therefore takes `canon teach stylesheet <topic>` per workspace first, which rewrites `assets/base.css`, and then this verb to embed the result. Running this one alone renders every lesson missing whatever the change added, and nothing reports it.
180
+
179
181
  A lesson missing one of the four marker pairs is refused by name rather than rewritten, and every other lesson in the run still rewrites. The record's `skipped` list carries the refused files and which marker each is missing.
180
182
 
181
183
  The `style` region carries the quiz stepper after the workspace stylesheet, for every lesson but the one shape named three paragraphs down, so it wins the cascade at equal specificity and reaches a workspace seeded before it existed. The stepper is CSS over the radio inputs a quiz is written from: it hides every question that follows one holding no checked input, and hides a question's feedback until that question does. The general sibling combinator is what keeps that from depending on the markup contract holding, since it selects the same questions as the adjacent one for a quiz written correctly and goes on gating when anything sits between two of them, where the adjacent one matches nothing and shows them all at once. Gating there rather than in a script is what keeps a later stem from answering an earlier question with nothing to bind, and the whole block sits inside `@supports selector(:has(*))`, so an engine without `:has()` renders every question rather than a stepper showing nothing. Feedback there follows the workspace stylesheet, shown by one that says nothing about `.fb` and hidden by one grown under the retired script.
@@ -293,7 +293,7 @@ This section is the corpus the coverage claim is measured against: every name `c
293
293
  | `canon:migration-context` | When `docs/` holds agent-flavored files belonging in `canon/context/` |
294
294
  | `canon:migration-superseded` | When a drift report names a `.claude/` file a folder has replaced |
295
295
  | `canon:migration-standards-drop` | When the project still holds an installed `.claude/standards/` tree |
296
- | `canon:canon-feedback-file` | When something in the toolkit is broken, missing, or off |
296
+ | `canon:canon-feedback` | When something in the toolkit is broken, missing, or off, to open an issue on it |
297
297
  | `canon:canon-feedback-triage` | In the toolkit repo, to work through the open feedback issues |
298
298
  | `canon:canon-rollout` | In the toolkit repo, to take one change out to every consuming project at once |
299
299
 
@@ -309,6 +309,7 @@ This section is the corpus the coverage claim is measured against: every name `c
309
309
  | `canon:draft-ready` | For finished files a worker should copy, written as a ready folder with its overview, thin plan, and task |
310
310
  | `canon:draft-context` | For a brand-new `canon/context/<domain>.md` entry, drafted against `standards/context.md` |
311
311
  | `canon:draft-wireframes` | For a brand-new `canon/wireframes/<surface>.md` file, drafted against `standards/wireframes.md` |
312
+ | `canon:draft-wiki` | For a brand-new wiki reference page on a subject owned outside the project, drafted against `standards/wiki.md` |
312
313
  | `canon:draft-figure` | For a hand-drawn figure inside an existing doc, drafted against `standards/figures.md` and rendered through Mermaid or freehand SVG |
313
314
  | `canon:draft-readme` | For a project's `README.md`, drafted against `standards/readme.md` |
314
315
  | `canon:bash-script` | For an interactive, human-facing shell tool |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "4.108.0",
4
+ "version": "4.110.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {