@erclx/canon 4.54.0 → 4.56.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 (37) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/canon-cli/SKILL.md +8 -1
  3. package/claude/skills/claude-autoship/SKILL.md +17 -1
  4. package/claude/skills/claude-orchestrate/references/orchestrator-dispatch.md +17 -7
  5. package/claude/skills/claude-worktree/SKILL.md +9 -3
  6. package/claude/skills/context-draft/REQUIREMENT.md +37 -0
  7. package/claude/skills/context-draft/SKILL.md +59 -0
  8. package/claude/skills/create-skill/REQUIREMENT.md +1 -1
  9. package/claude/skills/create-standard/SKILL.md +1 -1
  10. package/claude/skills/index-lookup/REQUIREMENT.md +35 -0
  11. package/claude/skills/index-lookup/SKILL.md +42 -0
  12. package/claude/skills/setup-init/SKILL.md +2 -1
  13. package/claude/skills/wireframe-draft/REQUIREMENT.md +38 -0
  14. package/claude/skills/wireframe-draft/SKILL.md +64 -0
  15. package/claude/skills/youtube-transcripts/SKILL.md +1 -1
  16. package/docs/agents/index.md +1 -1
  17. package/docs/agents/tasks.md +27 -1
  18. package/docs/target-projects.md +1 -1
  19. package/docs/workflow/ai-workflow.md +15 -12
  20. package/package.json +1 -1
  21. package/src/claude/cases/authoring.ts +10 -0
  22. package/src/claude/cases/misc.ts +4 -0
  23. package/src/commands/tasks.ts +97 -0
  24. package/src/gate/measures.ts +73 -0
  25. package/src/gate/stages.ts +11 -0
  26. package/src/tasks/answers.ts +42 -11
  27. package/src/tasks/branch.ts +78 -0
  28. package/src/web/readme-citations.ts +90 -0
  29. package/standards/branch.md +3 -1
  30. package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +1 -1
  31. package/tooling/nextjs/configs/eslint.config.js +90 -0
  32. package/tooling/nextjs/configs/next.config.ts +10 -0
  33. package/tooling/nextjs/configs/playwright.config.ts +27 -0
  34. package/tooling/nextjs/configs/vitest.config.ts +28 -0
  35. package/tooling/nextjs/manifest.toml +19 -0
  36. package/tooling/nextjs/reference.md +47 -0
  37. package/tooling/nextjs/seeds/.cspell/tech-stack.txt +3 -0
@@ -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.54.0",
4
+ "version": "4.56.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -56,6 +56,13 @@ Run `canon tooling sync <stack> <target> --check` for the list resolved against
56
56
  - `commitlint.config.js`
57
57
  - `scripts/verify.sh`
58
58
 
59
+ ### nextjs
60
+
61
+ - `eslint.config.js`
62
+ - `next.config.ts`
63
+ - `playwright.config.ts`
64
+ - `vitest.config.ts`
65
+
59
66
  ### python
60
67
 
61
68
  - `.coveragerc`
@@ -102,4 +109,4 @@ Run `canon tooling sync <stack> <target> --check` for the list resolved against
102
109
 
103
110
  ## Source of truth
104
111
 
105
- - Full semantics live in the toolkit's `.claude/context/tooling.md`, `.claude/context/standards/`, and `.claude/context/claude-plugin/`. This skill is the target-session summary. When they disagree, the context docs win.
112
+ - Full semantics live in the toolkit's own context entries. This skill is the target-session summary. When they disagree, the context docs win.
@@ -35,12 +35,28 @@ Invoke `canon:claude-worker` first, whatever the worktree state. This session is
35
35
 
36
36
  Report it rather than proceeding silently when `canon:claude-worker` does not resolve. It ships with the plugin, so a session running this chain from a project holding the CLI alone builds with no role asserted.
37
37
 
38
- If `git rev-parse --git-dir` equals `git rev-parse --git-common-dir`, the session is in the main worktree. Invoke `canon:claude-worktree` before continuing. The wrapper handles name derivation and branch alignment. Do not call `EnterWorktree` directly.
38
+ If `git rev-parse --git-dir` equals `git rev-parse --git-common-dir`, the session is in the main worktree. Invoke `canon:claude-worktree` before continuing, carrying the argument the subsection below derives. The wrapper handles branch alignment. Do not call `EnterWorktree` directly.
39
39
 
40
40
  If neither command resolves, stop: `❌ Not a git repository. Autoship needs git or a WorktreeCreate hook.`
41
41
 
42
42
  If the two commands differ, the session is already in a linked worktree. Continue.
43
43
 
44
+ ### Name the worktree from the plan rather than leaving it to be derived
45
+
46
+ This step runs ahead of Step 1, so what it holds is the raw invocation argument rather than a resolved plan. When that argument is a plan path or a bare slug, run the verb on it and hand the result to `canon:claude-worktree` as its tier 0 argument:
47
+
48
+ ```bash
49
+ canon tasks plan-branch <argument> --json
50
+ ```
51
+
52
+ - `conforms: true`: pass the record's `branch`, which is already `<type>/<slug>`, and invoke nothing else to derive a name.
53
+ - `conforms: false`: the plan's own filename breaks a cap in `${CLAUDE_SKILL_DIR}/../../standards/branch.md`. Pass `branch` anyway and say the cap it broke, since the alternative is a name this session shortened by hand, which is a second derivation and the thing this call exists to prevent. `git-branch` decides the rename at ship.
54
+ - Anything else, including a refusal, a record carrying no `branch` key, and an installed binary carrying no `plan-branch` subcommand: invoke `canon:claude-worktree` bare and let its ladder derive the name. Say the verb did not answer, so a reader can tell a derived name from a fallback one.
55
+
56
+ The dispatch runbook runs the same verb on the same plan to pick the branch its collision check clears, so calling it here is what makes the checked branch and the taken branch one string. Deriving a name from `<plan>` by reading it was the alternative, and it is what produced three strings for one plan across four dispatches on 2026-09-05.
57
+
58
+ A caller that supplied a task path, or supplied nothing at all, has no plan to hand the verb here, since resolving either is Step 1's work and Step 1 has not run. Invoke `canon:claude-worktree` bare in both cases. That is the ladder unchanged rather than a regression, and it leaves the hole open: a dispatched worker reaching this step through a task path derives its name from a tier rather than from the plan the dispatcher checked.
59
+
44
60
  ## Step 1: read the plan
45
61
 
46
62
  Resolve `<plan>` in this order, stopping at the first match:
@@ -7,9 +7,18 @@ Run this at loop step 4, for a `## Run now` row whose plan is verified, in place
7
7
 
8
8
  ## Derive the candidate
9
9
 
10
- Resolve `<slug>` from `<plan>`, the row's plan file, the way `claude-worktree` Step 2 resolves a plan-matched name, per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. Resolve `<type>` off that plan's `## Summary` and `**Files to touch:**` lines, per `${CLAUDE_SKILL_DIR}/../../standards/branch.md`, defaulting to `feat` when the lines settle nothing. The candidate branch is `<type>/<slug>`.
10
+ Run `canon tasks plan-branch <plan> --json` against the row's plan file and read `branch`, `type`, `slug`, and `conforms` off the record.
11
11
 
12
- This is the branch the worker takes, not a guess at one it will derive for itself. Carry the exact string into the launch below. Both halves of that derivation have already disagreed in production: one run checked `docs/remaining-skill-verdicts` against a worker that took `docs/skill-verdicts-decide`, and a later one checked `fix/path-form-hook` against a worker that took `feat/path-form-hook`. A check against a branch nobody uses verifies nothing, and a slug mismatch no longer fails the run downstream on its own, since `claude-autoship` now takes `<plan>` directly rather than resolving it from the worker's own branch. The check above is what has to catch a wrong candidate now.
12
+ - `conforms: true`: take `branch` as the candidate, and take `type` and `slug` from the same record for the check below.
13
+ - `conforms: false`: the plan's own filename breaks a cap in `${CLAUDE_SKILL_DIR}/../../standards/branch.md`. Report which, and hand the row to the human-launch line below rather than shortening the slug here. A rename parts the branch from the plan filename that `claude-worktree` tier 1 and `git-pr`'s plan lookup both read back.
14
+ - `reason: archived`, `no-plan`, or `bad-input`: the row does not cite a live plan. Repoint the row or fix the citation rather than dispatching, since `claude-autoship` Step 1 refuses the same file and the worker would meet that refusal after the launch spent.
15
+ - Anything else, including a record carrying no `branch` key and an installed binary carrying no `plan-branch` subcommand: treat the candidate as unverified rather than clear, name which reading could not be taken, and fall back to the human-launch line below. Re-deriving by prose here rebuilds the defect the verb closes, and does it quietly.
16
+
17
+ Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero.
18
+
19
+ The worker calls the same verb on the same plan at `claude-autoship` Step 0, so the branch this gate checks and the branch that session takes are one string by construction rather than two readings of one paragraph. They were two readings until 2026-09-06. One run checked `docs/remaining-skill-verdicts` against a worker that took `docs/skill-verdicts-decide`, another checked `fix/path-form-hook` against a worker that took `feat/path-form-hook`, and four dispatches on 2026-09-05 produced three strings for one plan. A check against a branch nobody uses verifies nothing.
20
+
21
+ The type the verb reports is fixed at `feat` whatever the row does, which is the half of the derivation that disagreed most. What makes that safe is that a branch type is cosmetic: `git-stage` reads a commit's type off the staged diff and `git-pr` reads a title off the diff, so nothing a release reads passes through the branch name. What it costs is a worktree listing where every dispatched branch reads `feat/`, which a person scanning one loses. Nothing renames it later, and this paragraph said `git-branch` did until 2026-09-06, when its conventions guard turned out to fire on a conforming `feat/` before reaching any type judgment.
13
22
 
14
23
  ## Check the plan waits on nobody
15
24
 
@@ -93,9 +102,9 @@ The worker resolves that id back to a name through `canon sessions list --json`,
93
102
 
94
103
  The template carries no worktree call. `claude-autoship` Step 0 invokes `canon:claude-worker` and then `canon:claude-worktree` itself, and neither carries the flag, so both are reachable through the `Skill` tool regardless of where a call to them would sit in a prompt. The autoship call carries `<plan>`, the same file this runbook already read to derive the branch, so its Step 1 takes it as the caller-supplied plan rather than re-deriving one from the slug the worker's branch happens to carry.
95
104
 
96
- Dropping the argument does not hand `claude-worktree` a formal one in its place. A worker launched onto `main` cannot match tier 1, a board carrying more than one plan puts tier 2 out of reach, and tier 3 tells it to ask a person who is not there, so the ladder alone still closes nothing.
105
+ The template names no branch, and it does not need to. `claude-autoship` Step 0 runs `canon tasks plan-branch <plan>` on the same file this runbook derived the candidate from, and hands the `<type>/<slug>` it reports to `claude-worktree` as its tier 0 argument, so the two sides agree by calling one derivation rather than by a string copied between them.
97
106
 
98
- What closes it is the same inference four workers already took before this template existed: the session already holds `<plan>` and derives `claude-worktree`'s name from it directly, rather than waiting on a tier to supply one. That is a judgment rather than a contract, and it is the same judgment both live disagreements came from, so read it as the residual risk this template still carries rather than as solved.
107
+ That retires the inference four workers took before the verb existed, which was to derive the name from `<plan>` by their own reading of it. Nothing has to reach `claude-worktree`'s ladder now, which mattered because a worker launched onto `main` cannot match tier 1, a board carrying more than one plan puts tier 2 out of reach, and tier 3 tells it to ask a person who is not there. What still travels on judgment is the fallback: a worker whose installed binary carries no `plan-branch` derives by prose, which is where both live disagreements came from.
99
108
 
100
109
  ### Expansion needs position zero and a clean delimiter, not leading order alone
101
110
 
@@ -157,9 +166,10 @@ controller that hands an operator two chained blocks to paste as separate
157
166
  messages risks both landing as one, where everything after the first
158
167
  command's name is read as that command's own argument and the second command
159
168
  never fires, measured four times out of four on 2026-09-02.
160
- `.claude/context/claude-plugin/skill-lifecycle.md` carries the mechanism. The
161
- fix is what the template above already takes: one message, one command, at
162
- position zero.
169
+ A slash command expands as a user invocation only at position zero of a
170
+ prompt, and a later one in the same text reaches the session as prose instead.
171
+ The fix is what the template above already takes: one message, one command,
172
+ at position zero.
163
173
 
164
174
  ### What the brief may carry
165
175
 
@@ -49,11 +49,17 @@ Tier 0 sits ahead of the inference because every tier below it answers from stat
49
49
 
50
50
  Validate the result: letters, digits, dots, underscores, dashes only, max 64 chars (`/` separators are also allowed). If the derived name violates the rule, sanitize by replacing invalid chars with `-` and truncating. Show the sanitized name in the preview before invoking.
51
51
 
52
- Resolve `<type>` here as well, since Step 3 previews it and Step 5 renames onto it, drawing the value from the type vocabulary in `${CLAUDE_SKILL_DIR}/../../standards/branch.md`. A type the caller spelled in tier 0 wins outright and no reading overrides it. A name from a plan takes the type that plan's own work carries, read off its `## Summary` and `**Files to touch:**` lines. Every other case takes `feat`, which covers a name from a branch, a bare name from the user, and a plan whose lines settle nothing.
52
+ Resolve `<type>` here as well, since Step 3 previews it and Step 5 renames onto it, drawing the value from the type vocabulary in `${CLAUDE_SKILL_DIR}/../../standards/branch.md`. A type the caller spelled in tier 0 wins outright and no reading overrides it. A name that came from a plan, through tier 1 or tier 2, takes its type from `canon tasks plan-branch <plan> --json`, read off the record's `type` field. Every other case takes `feat`, which covers a name from a branch, a bare name from the user, and a plan the verb could not answer for.
53
53
 
54
- The caller's type wins because reading it off a plan is the half that has already disagreed in production. One dispatch checked `fix/path-form-hook` and the worker took `feat/path-form-hook`, both sides reading the same plan and grading it differently.
54
+ The verb is the reading rather than this body, because a type read off a plan's `## Summary` and `**Files to touch:**` lines is a judgment, and it has disagreed with itself in production. One dispatch checked `fix/path-form-hook` and the worker took `feat/path-form-hook`, both sides reading the same plan and grading it differently. The verb answers `feat` for every plan, so two sides calling it cannot part.
55
55
 
56
- A wrong type is cheap. `git-branch` renames to conventional format later in the same chain and runs ahead of `git-pr`, so a `feat/` written over a fix is corrected before any pull request opens.
56
+ The caller's type still wins over the verb's, because tier 0 is the one source that knows something no file states. The ordinary caller is `claude-autoship` Step 0, which ran the verb itself and is handing over the answer it got, so nothing is overridden in that case either.
57
+
58
+ Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero. Take `feat` and say the verb did not answer where it refuses, where the record carries no `type` key, or where the installed binary carries no `plan-branch` subcommand.
59
+
60
+ A wrong type is cheap because the branch type is cosmetic rather than because anything corrects it. `git-stage` reads a commit's type off the staged diff and `git-pr` reads a title off the diff too, so the semantics a release reads never pass through the branch name at all. Nothing downstream is wrong when a `feat/` sits over a fix, and a person scanning a worktree listing loses a signal.
61
+
62
+ `git-branch` does not correct it, which three surfaces used to say it did. Its second guard reads `If branch name already follows conventions, stop`, and `${CLAUDE_SKILL_DIR}/../../standards/branch.md` makes the type vocabulary an axis without making the choice within that vocabulary one, so `feat/` over a fix conforms and the guard fires ahead of the Analysis line that would have re-derived the type.
57
63
 
58
64
  Then test both names the entry is about to claim. Neither read needs a worktree, and a stop after Step 4 leaves one built with the session sitting inside it, so both belong here rather than beside the rename:
59
65
 
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: context-draft
3
+ description: Why a brand-new .claude/context/<domain>.md entry needs a catalog collision check and a confirm step, not the refresh path claude-docs already owns
4
+ ---
5
+
6
+ # Context draft requirement
7
+
8
+ ## Gap
9
+
10
+ Without this skill, a session documenting a domain that has no context entry yet either invents a shape from memory or reaches for the refresh mechanism, which reads an existing entry and a diff and has neither to work from for a domain that never had one. Either way the entry ships with no read of `standards/context.md` and no check against the catalog for a sibling already covering the same ground under a different name.
11
+
12
+ ## Must
13
+
14
+ - Read `standards/context.md` before drafting, since the frontmatter contract and the three-question test are what make the entry arguable against a sibling
15
+ - Check the domain against the existing catalog by slug and by title-and-description, not by slug alone, since a domain can be covered under a name the topic phrase does not guess
16
+ - Default a brand-new entry to a flat file, since a fresh domain never holds the three or more sub-areas the standard requires before it earns a folder
17
+ - Confirm the resolved path and the full content with the user before writing, since placement is a judgment call with no diff to preview it against
18
+ - Run `canon indexes regen` on `.claude/context/` after writing, so the catalog picks up the new domain immediately rather than drifting until the next unrelated regen
19
+
20
+ ## Must not
21
+
22
+ - Refresh or rewrite an existing entry. A domain already covered refuses toward the refresh mechanism.
23
+ - Hand-edit `.claude/context/index.md`. It regenerates from sibling frontmatter, and a hand edit is overwritten on the next regen.
24
+ - Assume this skill's own invocation frequency needs no check. Whether anything reaches for it beyond an author typing its name has no answer at creation time, so a review pass some months in should read that back rather than take the assumption on faith.
25
+
26
+ ## Guards
27
+
28
+ - No domain given: stop and ask what domain the entry should cover.
29
+ - The derived slug already resolves to `.claude/context/<slug>.md` or `.claude/context/<slug>/index.md`: stop and point at the refresh mechanism instead.
30
+ - The catalog already covers the domain under a different name: stop the same way, checked against the titles and descriptions the Placement step already reads.
31
+
32
+ ## Out of scope
33
+
34
+ - Refreshing an existing `.claude/context/<domain>.md` entry against a diff: `claude-docs`
35
+ - Drafting a `.claude/wireframes/<surface>.md` file: `wireframe-draft`
36
+ - Drafting a `docs/*.md` page: `docs-draft`
37
+ - Drafting a standard, a snippet, or a governance rule: `create-standard`, `create-snippet`, `create-rule`
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: context-draft
3
+ description: Drafts a brand-new `.claude/context/<domain>.md` entry against `standards/context.md`, checks the catalog for a name-or-topic collision, decides flat-file placement, confirms with the user, then writes. Use when asked to "write a context entry for X", "document the X domain", "add a context entry for X", or "create a .claude/context page for X" where no existing entry covers the domain. Do NOT use to refresh an existing entry against a diff, which is `claude-docs`.
4
+ ---
5
+
6
+ # Context draft
7
+
8
+ Drafts one brand-new `.claude/context/<domain>.md` entry end to end: read the standard, decide whether the domain is already covered under a different name, confirm the draft with the user, then write.
9
+
10
+ Read these files in parallel:
11
+
12
+ - `${CLAUDE_SKILL_DIR}/../../standards/context.md`: the three questions an entry must answer, its frontmatter, required and expected sections, and what stays out of it
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
+ ## Guards
17
+
18
+ - If no domain is given, stop: `❌ No domain given. Name the domain this entry should cover.`
19
+ - Derive a kebab-case slug from the domain and check whether `.claude/context/<slug>.md` or `.claude/context/<slug>/index.md` already exists. Either resolving means the domain is already covered under that exact name. Stop: `❌ <slug> already has a context entry. Use claude-docs to refresh it instead.`
20
+
21
+ ## Placement
22
+
23
+ - Read `.claude/context/index.md` and check every title and description it lists against the domain. The Guards check above only catches an exact-slug collision, and a domain already covered under a different name still resolves here at no extra cost, since this read already runs.
24
+ - Stop the same way on a match: `❌ <path> already covers this domain under a different name. Use claude-docs to refresh it instead.`
25
+ - Default a brand-new entry to a flat file, `.claude/context/<slug>.md`. A domain starts as one page's worth of narrative, and the standard only splits it into a folder once it holds three or more sub-areas, which a fresh domain never does on day one.
26
+
27
+ ## Draft
28
+
29
+ - Read the domain's own folders and files well enough to answer the standard's three questions: where things live, why they are that way, and how to add one more of what the domain holds.
30
+ - Draft `title` and `description` frontmatter, then `## Overview`, `## Layout` (folder ownership lines only, never a file-by-file list), and `## Decisions` or `## Gotchas` wherever the domain's history supplies a non-obvious choice or a workaround worth preserving. Omit an expected section with nothing to put in it rather than padding it.
31
+ - Draft against `${CLAUDE_SKILL_DIR}/../../standards/context.md`'s template and ordering: `Overview`, `Layout`, `Decisions`, `Gotchas`, then anything else.
32
+
33
+ ## Confirm
34
+
35
+ - Show the resolved path and the full drafted content before writing.
36
+ - Confirm both with the user. This skill waits for that answer rather than treating the tool permission dialog as the gate, since placement here is a judgment call with no diff to preview it against.
37
+
38
+ ## Write
39
+
40
+ - Write the file at the confirmed path, creating the folder when it is absent.
41
+ - Run `canon markdown audit <path>`.
42
+ - Run `canon indexes regen .claude/context`, so the domain appears in `.claude/context/index.md` immediately rather than drifting until the next unrelated regen.
43
+
44
+ ## Response format
45
+
46
+ ### Preview
47
+
48
+ **Domain:** `<domain>`
49
+ **Placement:** `<path>`
50
+
51
+ ```markdown
52
+ <drafted frontmatter and body>
53
+ ```
54
+
55
+ ### After confirmation
56
+
57
+ ```plaintext
58
+ ✅ Drafted: <path>
59
+ ```
@@ -35,4 +35,4 @@ A third failure is a folder created for a moment a rule, a verb, or an existing
35
35
  ## Out of scope
36
36
 
37
37
  - Editing an existing skill, which the authoring standard and the skill's own requirement govern
38
- - Whether anything ends up invoking the new skill: the usage census in `.claude/context/claude-plugin/skill-strategy.md`, read once the skill has run
38
+ - Whether anything ends up invoking the new skill: the usage census the toolkit's own context entry tracks, read once the skill has run
@@ -23,7 +23,7 @@ Creates one standard file. Read these files in parallel:
23
23
 
24
24
  Emit the full path on its own line.
25
25
 
26
- - In the toolkit: this is the authoring source. Remind the user to run `bun run check` to regenerate the consumed copy under `.claude/`. That pass also regenerates the `standards/index.md` entry, and the user adds a row to the standards table in `.claude/context/standards/overview.md`.
26
+ - In the toolkit: this is the authoring source. Remind the user to run `bun run check` to regenerate the consumed copy under `.claude/`. That pass also regenerates the `standards/index.md` entry, and the user adds a row to the standards table in `.claude/context/standards/overview.md`. <!-- canon-allow-reference: audience-branched, only a toolkit-repo reader reaches this line and that reader can open the path -->
27
27
  - In a project: the file is project-local and no toolkit command touches it. Remind the user to copy it to the toolkit repo, under `standards/<slug>.md`, if it should ship to every project.
28
28
 
29
29
  Separate the two by whether the folder already held toolkit-authored standards before this write, since a project authoring its own uses the same path.
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: index-lookup
3
+ description: Why a topic search over the tracked index catalog needs its own skill, and why a folder outside `list`'s walk gets a named pointer instead of a search hit
4
+ ---
5
+
6
+ # Index lookup requirement
7
+
8
+ ## Gap
9
+
10
+ Without this skill, `canon indexes list --json` exists as a verb with nobody wrapping it into a topic-search request. A session either greps the tree by hand, which misses everything the catalog already states in `title` and `description` rather than in file contents, or answers from whichever folder it happened to open, which reports a topic as undocumented when the topic only sits outside the walked corpus: a gitignored indexed folder such as `.canon/tasks/` or `.canon/memory/`, or a README-based record catalog such as `.canon/groundwork/`, neither of which `list` ever reaches.
11
+
12
+ ## Must
13
+
14
+ - Run `canon indexes list --json` and match the topic against every entry's `title`, `description`, and `path`
15
+ - Check whether the project carries a gitignored indexed folder or a README-based record catalog outside `list`'s walk before naming any of them, since a folder absent from the project names nothing
16
+ - Report zero hits inside the walked corpus as zero hits, never as "not documented," since a folder outside the walk can still hold the answer
17
+
18
+ ## Must not
19
+
20
+ - Search file contents. A miss against `title`, `description`, and `path` is the whole answer, and grepping the tree is a different request.
21
+ - Extend `canon indexes list` to read a `README.md` catalog or to bypass the gitignore filter. Both are accepted trade-offs the verb already carries, and widening either is a CLI change, not a skill-body change.
22
+ - Regenerate an index on its own initiative. Reporting a stale-looking entry is this skill's job, and writing the fix belongs to `canon indexes regen`.
23
+ - Fire from a request to browse a file whose path is already known, or to search source code, both of which this skill answers nothing for
24
+ - Assume this skill's own invocation frequency needs no check. The reminder hook names it beside `canon indexes list --json`, and a session recognizing a topic-lookup request can trigger it by description, but whether anything reaches for it beyond an operator typing its name has no answer at creation time, so a review pass some months in should read that back rather than take it on faith.
25
+
26
+ ## Guards
27
+
28
+ - `canon indexes list` does not resolve on an older install. Report that rather than falling back to a manual walk, since a hand-rolled walk here restates the CLI logic this skill exists to call.
29
+ - A folder failing frontmatter validation drops out of `entries` and its message lands in `errors`, per the verb's own per-folder isolation. Report the error alongside the match rather than treating it as a reason to stop.
30
+
31
+ ## Out of scope
32
+
33
+ - Regenerating or scaffolding an index, which `canon indexes regen` and the `setup-indexes` skill own
34
+ - Reading a folder's contents once a hit names it, which is an ordinary file read rather than part of the lookup
35
+ - Chaining this skill into `setup-indexes`'s bootstrap flow as a verification step. Left independent: `setup-indexes` already validates through `canon indexes regen --dry-run`, and a second skill call inside a bootstrap flow duplicates a check that already runs.
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: index-lookup
3
+ description: Answers a topic search over every tracked index.md catalog in the project by running `canon indexes list --json` and matching the topic against each entry's title, description, and path, then reports the hits or names why there are none. Names the gitignored task, memory, and diagram catalogs and the groundwork and intake record folders as pointers outside its walked corpus, so it never claims a topic is undocumented when the topic only sits there. Use when asked "where is X documented", "is there a page about X", "search the docs for X", "find the index entry for X", or "what covers X". Do NOT use to grep source code, browse a file whose path is already known, or regenerate an index, which is `canon indexes regen`.
4
+ ---
5
+
6
+ # Index lookup
7
+
8
+ Answers a topic search over the project's tracked `index.md` catalogs, not a text search over file contents. Matches a topic string against every entry's `title`, `description`, and `path`, drawn from wherever `canon indexes list --json` walks.
9
+
10
+ ## Steps
11
+
12
+ 1. Resolve the project root with `git rev-parse --show-toplevel`, falling back to the working directory when the read fails.
13
+ 2. Run `canon indexes list --json` from that root.
14
+ 3. Read `errors` first. A folder failing frontmatter validation drops out of `entries` and lands its message in `errors` instead, per the verb's own per-folder isolation, so report an error alongside the match rather than reading it as a reason to stop.
15
+ 4. Match the topic case-insensitively as a substring against each entry's `title`, `description`, and `path`. Report every match. A lookup with no ranking is honest about what it found, and a single best guess is not.
16
+ 5. Check whether the project carries a gitignored indexed folder or a README-based record catalog outside `list`'s walk: `.canon/tasks/`, `.canon/memory/`, `.canon/diagrams/`, `.canon/groundwork/`, `.canon/intake/`. Test each for existence and at least one file inside it before naming it. A folder absent from this project names nothing.
17
+ 6. Report the hits, plus a pointer to any outside-the-walk folder found present, per Output below.
18
+
19
+ ## Rules
20
+
21
+ - Report zero hits as zero hits, never as "not documented." The topic can sit inside a folder step 5 found and named, so an empty walked corpus is a fact about the search, not about the project.
22
+ - Never search file contents. A miss against `title`, `description`, and `path` is the whole answer, and a request that needs a text search calls for grepping the tree instead.
23
+ - Never regenerate or edit an index. Report an entry that reads as stale rather than running `canon indexes regen` on this skill's own initiative.
24
+ - Report an absent `canon indexes list` subcommand rather than falling back to a manual walk. It ships with the CLI, so a project on an older install meets a missing verb, and a hand-rolled walk here restates the CLI logic this skill exists to call instead.
25
+
26
+ ## Output
27
+
28
+ ```plaintext
29
+ Index lookup: "<topic>"
30
+
31
+ Hits:
32
+ - <path>: <title> — <description>
33
+
34
+ Outside the walked corpus (check by hand):
35
+ - <folder>: <one-line reason it sits outside list's walk>
36
+ ```
37
+
38
+ Replace the "Hits" block with `No catalog entry matched "<topic>".` when nothing matched. Omit "Outside the walked corpus" when the project carries none of the five folders from step 5, or when every one it carries is empty.
39
+
40
+ ## Reference
41
+
42
+ Run `canon docs indexes` for the `canon indexes list` flags, exit codes, and JSON shape. It resolves from the toolkit rather than from the target's own tree.
@@ -45,7 +45,8 @@ Read these from the project root in parallel, skipping any that do not exist:
45
45
  ## Resolve arguments
46
46
 
47
47
  - **Stack:** pick the closest governance stack by matching detected runtime or framework against stack names in the catalog. If nothing matches, fall back to `base` and carry the fallback into the preview.
48
- - **Tooling stack:** pick the closest tooling stack from `canon tooling list --json` (e.g. `vite-react`, `astro`). Distinct from the governance stack. Fall back to `base` if no framework match, and carry that fallback into the preview too.
48
+ - **Tooling stack:** pick the closest tooling stack from `canon tooling list --json` (e.g. `vite-react`, `astro`, `nextjs`). Distinct from the governance stack. Fall back to `base` if no framework match, and carry that fallback into the preview too.
49
+ - **Next.js:** map `next` in `package.json` dependencies, or a root `next.config.ts`/`.js`/`.mjs` file, to the `nextjs` tooling stack. The dependency name does not match the stack name by itself.
49
50
  - **Extras:** identify technologies not already covered by the picked stack. For each, find a rule whose `description` or `paths` points at that technology and pass it via `--add`. Do not add a rule the stack already pulls in.
50
51
  - **Skip (`--skip`):** `wiki` installs by default. Add `--skip wiki` only when the user explicitly wants it left out.
51
52
 
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: wireframe-draft
3
+ description: Why a brand-new .claude/wireframes/<surface>.md file needs a real draft and a tier-detect step, not the stub the wireframe-sweep already writes
4
+ ---
5
+
6
+ # Wireframe draft requirement
7
+
8
+ ## Gap
9
+
10
+ Without this skill, a session drafting a wireframe for a surface with no file yet either invents an ASCII layout from memory with no read of `standards/wireframes.md`, or waits on the wireframe-sweep, which only fires against a diff and only ever writes a bare `TODO` stub rather than a real layout, copy, and behavior draft. Neither path checks whether a visual-design tier above ASCII is already in play for the project before drafting the tier-0 file.
11
+
12
+ ## Must
13
+
14
+ - Read `standards/wireframes.md` before drafting, since the frontmatter contract, the layout and variant rules, and the Transcription-wireframes branch are what make the file arguable against a sibling
15
+ - Walk the whole `.claude/wireframes/` tree, including a grouped surface's own subfolder, before drafting, since a top-level-only check misses a nested match
16
+ - Detect an existing higher tier from `.claude/DESIGN.md` and the wireframes tree and report it, never build a companion render for it, since no shipped mechanism produces one
17
+ - Draft in transcription mode, citing the real source, when the surface names an already-built component. Draft in role-intent mode otherwise.
18
+ - Confirm the resolved path, the detected tier, and the full content with the user before writing, since the mode decision and the tier read are judgment calls with no diff to preview either against
19
+ - Run `canon indexes regen` on `.claude/wireframes/` after writing, so the catalog picks up the new surface immediately rather than drifting until the next unrelated regen
20
+
21
+ ## Must not
22
+
23
+ - Build a tier-1 or tier-2 rendering mechanism, an Excalidraw round trip, or any companion render. Detecting and reporting a higher tier is the whole of this skill's reach past ASCII.
24
+ - Hand-edit `.claude/wireframes/index.md`. It regenerates from sibling frontmatter, and a hand edit is overwritten on the next regen.
25
+ - Assume this skill's own invocation frequency needs no check. Whether anything reaches for it beyond an author typing its name has no answer at creation time, so a review pass some months in should read that back rather than take the assumption on faith.
26
+
27
+ ## Guards
28
+
29
+ - No surface given: stop and ask what surface the wireframe should cover.
30
+ - The derived slug already matches a file anywhere in the `.claude/wireframes/` tree, flat or nested: stop and point at editing that file directly, since no dedicated skill owns a full rewrite of an existing surface.
31
+
32
+ ## Out of scope
33
+
34
+ - Stubbing a surface a diff touched, or reporting drift in an existing wireframe against a diff: `claude/skills/claude-docs/references/wireframe-sweep.md`
35
+ - Drafting a `.claude/context/<domain>.md` entry: `context-draft`
36
+ - Drafting a `docs/*.md` page: `docs-draft`
37
+ - Drafting a standard, a snippet, or a governance rule: `create-standard`, `create-snippet`, `create-rule`
38
+ - Building or wiring an actual tier-1 or tier-2 rendering pipeline: the visual design workflow's own tier guidance, out of reach of a hand-drafting skill
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: wireframe-draft
3
+ description: Drafts a brand-new `.claude/wireframes/<surface>.md` file against `standards/wireframes.md`, walks the tree for a name collision, detects an existing higher visual-design tier without building one, confirms with the user, then writes. Use when asked to "draft a wireframe for X", "write the wireframe for this surface", "add a .claude/wireframes entry for X", or "wireframe this screen" where no surface file covers it yet. Do NOT use to fix a stale TODO stub or report wireframe drift against a diff, which is `claude-docs`'s wireframe-sweep step.
4
+ ---
5
+
6
+ # Wireframe draft
7
+
8
+ Drafts one brand-new `.claude/wireframes/<surface>.md` file end to end: read the standard, decide whether the surface is already covered, detect but never build a higher visual-design tier, confirm the draft with the user, then write.
9
+
10
+ Read these files in parallel:
11
+
12
+ - `${CLAUDE_SKILL_DIR}/../../standards/wireframes.md`: the three questions a wireframe must answer, its frontmatter, layout and variant rules, the Transcription-wireframes branch, and what moves to a context entry instead
13
+ - `${CLAUDE_SKILL_DIR}/../../standards/markdown.md`: banned words, punctuation, and formatting for the prose around the fences
14
+ - The `write-human` skill: voice, rhythm, and sentence construction for the prose around the fences
15
+
16
+ This skill stays fully independent of `claude/skills/claude-docs/references/wireframe-sweep.md`, which only ever writes a bare `TODO` stub for a surface a diff touched and reports drift against one a wireframe already covers. Neither the stub nor the drift check is a draft, and this skill never reads or writes through that mechanism.
17
+
18
+ ## Guards
19
+
20
+ - If no surface is given, stop: `❌ No surface given. Name the surface this wireframe should cover.`
21
+ - Derive a kebab-case slug from the surface. Walk the whole `.claude/wireframes/` tree, including a surface nested inside a grouped subfolder, rather than checking the top level alone. A match at any depth means the surface already has a file. Stop: `❌ <path> already covers this surface. Edit it directly; this skill only drafts a surface with no file yet.`
22
+
23
+ ## Tier detection
24
+
25
+ - Read `.claude/DESIGN.md` and every existing `.claude/wireframes/` file for a tier signal: a Stitch, Excalidraw, or Figma reference, or a marker naming one of them.
26
+ - State the detected tier at the confirm step. Always draft the tier-0 ASCII file regardless of what is detected, since that is the only shape this skill or any other shipped mechanism produces. Report a higher tier rather than attempting a companion render for it.
27
+ - Default silently to tier 0 when nothing is detected.
28
+
29
+ ## Draft
30
+
31
+ - Decide the mode before drafting. When the surface names an already-built component or file, open that source and draft in transcription mode, citing the render function, the stylesheet rule, or the built file each region and label traces to, per the standard's Transcription-wireframes section.
32
+ - Draft in role-intent mode otherwise: label each region by its role, never by a class name or a token value.
33
+ - Draft `title` and `description` frontmatter, then one `##` heading per layout variant, each holding its own ASCII `plaintext` fence with `←` role annotations, followed by `## Copy` and `## Behavior` sections against `${CLAUDE_SKILL_DIR}/../../standards/wireframes.md`'s template.
34
+ - Add a second layout variant only when the layout itself changes across a breakpoint or state, never for a spacing difference alone.
35
+ - Leave out algorithms, event-handler code, framework prop or class names outside transcription mode, and anything else the standard sends to a context entry instead.
36
+
37
+ ## Confirm
38
+
39
+ - Show the resolved path, the detected tier, and the full drafted content before writing.
40
+ - Confirm all three with the user. This skill waits for that answer rather than treating the tool permission dialog as the gate, since the mode decision and the tier read are judgment calls with no diff to preview either against.
41
+
42
+ ## Write
43
+
44
+ - Write the file at the confirmed path, creating a grouped subfolder only when the surface belongs beside siblings that already share one.
45
+ - Run `canon markdown audit <path>` against the prose outside the fenced block.
46
+ - Run `canon indexes regen .claude/wireframes`, so the surface appears in `.claude/wireframes/index.md` immediately rather than drifting until the next unrelated regen.
47
+
48
+ ## Response format
49
+
50
+ ### Preview
51
+
52
+ **Surface:** `<surface>`
53
+ **Placement:** `<path>`
54
+ **Detected tier:** `<tier-0 | tier-1 | tier-2 | none detected>`
55
+
56
+ ```markdown
57
+ <drafted frontmatter and body>
58
+ ```
59
+
60
+ ### After confirmation
61
+
62
+ ```plaintext
63
+ ✅ Drafted: <path>
64
+ ```
@@ -5,7 +5,7 @@ description: Fetches a YouTube video transcript with metadata frontmatter into t
5
5
 
6
6
  # YouTube transcripts
7
7
 
8
- Turn a pasted YouTube URL into a markdown file with YAML frontmatter and a cleaned prose body. The `canon transcripts` command owns the fetch, VTT cleanup, and frontmatter. Do not reimplement that logic. The output format and field list live in the toolkit's `.claude/context/transcripts.md`, which a target does not receive.
8
+ Turn a pasted YouTube URL into a markdown file with YAML frontmatter and a cleaned prose body. The `canon transcripts` command owns the fetch, the VTT cleanup, and the frontmatter shape. Do not reimplement or restate any of it here.
9
9
 
10
10
  ## Guards
11
11
 
@@ -43,7 +43,7 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
43
43
  - [State-scoped risk](state-scoped-risk.md): Reading committed state rather than an arriving change, the shipped-tree corpus the secret scan reads, what it keys on and how a deliberate value is exempted, the advisory check and its network failure mode, and why one gates while the other reports
44
44
  - [Superseded values](superseded.md): Reading where the tree still asserts a value a changed convention no longer produces, why the sweep keys on the value rather than the file, the family stem behind a templated citation, the exemption marker, the blind spots it cannot reach, and why it reports rather than gates
45
45
  - [Targets](targets.md): The projects this toolkit installed into, the record the install writes against the sweep that backs it, what bounds each answer, and the cross-target pull request read
46
- - [Tasks](tasks.md): Selecting a shipped task by stem or pull request, recording a number and closing an outcome, the refusal reasons, the board and backlog checks validate runs, and why the board root defaults to the main worktree
46
+ - [Tasks](tasks.md): Selecting a shipped task by stem or pull request, recording a number and closing an outcome, deriving the branch a dispatch and a worker both take, the refusal reasons, the board and backlog checks validate runs, and why the board root defaults to the main worktree
47
47
  - [Teach](teach.md): Listing learning workspaces with what their records schedule next, opening one with its required files, recording sources and glossary terms, resolving what the next lesson needs before it is written, rewriting the root listing, a contents page, and each lesson's chrome and quiz stepper from its marker regions, the refusal reasons, and why every write here runs through a verb
48
48
  - [Test order](test-order.md): Reading where an implementation reached history ahead of its test, how a pair is decided, the three verdicts, the coverage the pairing cannot reach, and why the check reports rather than gates
49
49
  - [Worktrees](worktrees.md): Reporting which worktrees are reclaimable, removing the ones that are, the record a hook reads back, why the reading keys on the pull request rather than on git ancestry, the refusals it names, and the two removal shapes
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Tasks
3
- description: Selecting a shipped task by stem or pull request, recording a number and closing an outcome, the refusal reasons, the board and backlog checks validate runs, and why the board root defaults to the main worktree
3
+ description: Selecting a shipped task by stem or pull request, recording a number and closing an outcome, deriving the branch a dispatch and a worker both take, the refusal reasons, the board and backlog checks validate runs, and why the board root defaults to the main worktree
4
4
  ---
5
5
 
6
6
  # Tasks
@@ -104,6 +104,32 @@ The orchestrator dispatch runbook calls this before it checks the branch or the
104
104
  canon tasks plan-answers dispatch-answer-gate --json | jq -r '.launchable'
105
105
  ```
106
106
 
107
+ ## Plan branch
108
+
109
+ `canon tasks plan-branch <plan>` derives the branch name from a plan file. It reports and never writes, and it names the plan the same two ways `canon tasks plan-answers` does, by path or by slug, against the same two bases.
110
+
111
+ ```bash
112
+ canon tasks plan-branch dispatch-answer-gate
113
+ canon tasks plan-branch .canon/plans/feature-dispatch-answer-gate.md --json
114
+ ```
115
+
116
+ | Option | Effect |
117
+ | --------------- | ------------------------------------------- |
118
+ | `--json` | Emit a machine-readable record on stdout |
119
+ | `--root <path>` | Board root, defaulting to the main worktree |
120
+
121
+ The record carries `type`, `slug`, `branch`, `words`, and `conforms`. Exit codes: `0` derived and conforming, `1` refused with `no-plan`, `archived`, or `bad-input`, `2` derived with `conforms` false. Branch on `conforms` rather than on the exit code, which a shell function wrapping `canon` can flatten to zero.
122
+
123
+ `slug` is the plan filename with its `feature-` prefix and its extension taken off, and `type` is the constant `feat`. Reading a type off the plan's prose was the alternative, and it is the half of the derivation that has already disagreed with itself: one dispatch checked `fix/path-form-hook` against a worker that took `feat/path-form-hook`, both sides reading one plan. What makes the constant safe is that a branch type is cosmetic. `git-stage` reads a commit's type off the staged diff and `git-pr` reads a title off the diff, so the semantics a release reads never pass through the branch name. What it costs is a worktree listing where every plan-derived branch reads `feat/`, and nothing renames it later.
124
+
125
+ `conforms` reads both caps `standards/branch.md` states, being 4 words on the description and 50 characters on the whole branch. A false reading is a row for a person rather than a name to shorten here, since a rename parts the branch slug from the plan slug that `claude-worktree` tier 1 and `git-pr`'s plan lookup both read back.
126
+
127
+ Both sides of a dispatch call it. The orchestrator's collision check derives its candidate here, and `claude-autoship` Step 0 derives the worktree it enters from the same plan, so the branch a gate clears and the branch a session takes are one string by construction. They were two readings of one paragraph until 2026-09-06, when four dispatches on one plan produced three different strings.
128
+
129
+ ```bash
130
+ canon tasks plan-branch dispatch-answer-gate --json | jq -r '.branch'
131
+ ```
132
+
107
133
  ## Plan link
108
134
 
109
135
  `canon tasks plan-link <task> <plan>` writes or corrects a task's `Plan:` line, as `Plan: [<label>](<target>)` right after the H1. `claude-feature` calls it right after a plan file lands, when Step 1 resolved an existing task for the feature, so the line is a mechanical write rather than hand-edited markdown.
@@ -83,7 +83,7 @@ Each diagram entry records the commit and date it was last verified against, and
83
83
 
84
84
  The default path is `base`. `canon init` on `base` installs base tooling configs, Claude seeds, and governance core rules, and scaffolds an empty `.claude/wiki/`. Most projects need nothing more.
85
85
 
86
- Escalate only for real web apps. The `setup-init` skill reads `package.json` and root configs, then picks the matching tooling stack (`vite-react` today) and the matching governance stack (`react`, `astro`, `node`).
86
+ Escalate only for real web apps. The `setup-init` skill reads `package.json` and root configs, then picks the matching tooling stack (`canon tooling list --json` names the current set) and the matching governance stack (`react`, `astro`, `node`).
87
87
 
88
88
  `node-server` is named rather than detected. It carries the server-side security and persistence rules for a project writing request handlers or a persistence layer in TypeScript, and the detect step matches a runtime or a framework against stack names, so nothing there marks a project as a backend. Pass it deliberately with `canon init --stack node-server` or `canon gov install node-server <target>`.
89
89
 
@@ -284,24 +284,27 @@ This section is the corpus the coverage claim is measured against: every name `c
284
284
 
285
285
  ### Generate an artifact on demand
286
286
 
287
- | Skill | When to use |
288
- | -------------------------- | --------------------------------------------------------------------- |
289
- | `canon:create-rule` | For a project-specific governance rule the toolkit does not ship |
290
- | `canon:create-skill` | For a new `SKILL.md` |
291
- | `canon:create-snippet` | For a reusable prompt |
292
- | `canon:create-standard` | For a new authoring convention |
293
- | `canon:docs-draft` | For a brand-new `docs/*.md` page, drafted against `standards/docs.md` |
294
- | `canon:bash-script` | For an interactive, human-facing shell tool |
295
- | `canon:bash-cli-script` | For a non-interactive automation, CI, or pipeline script |
296
- | `canon:ci-workflow` | For a GitHub Actions workflow file |
297
- | `canon:canon-slides-draft` | For a deck, drafted as `.claude/SLIDES.md` and rendered to PowerPoint |
298
- | `canon:canon-screencast` | For a recording script with beats and defaults already seeded |
287
+ | Skill | When to use |
288
+ | -------------------------- | ------------------------------------------------------------------------------------------------- |
289
+ | `canon:create-rule` | For a project-specific governance rule the toolkit does not ship |
290
+ | `canon:create-skill` | For a new `SKILL.md` |
291
+ | `canon:create-snippet` | For a reusable prompt |
292
+ | `canon:create-standard` | For a new authoring convention |
293
+ | `canon:docs-draft` | For a brand-new `docs/*.md` page, drafted against `standards/docs.md` |
294
+ | `canon:context-draft` | For a brand-new `.claude/context/<domain>.md` entry, drafted against `standards/context.md` |
295
+ | `canon:wireframe-draft` | For a brand-new `.claude/wireframes/<surface>.md` file, drafted against `standards/wireframes.md` |
296
+ | `canon:bash-script` | For an interactive, human-facing shell tool |
297
+ | `canon:bash-cli-script` | For a non-interactive automation, CI, or pipeline script |
298
+ | `canon:ci-workflow` | For a GitHub Actions workflow file |
299
+ | `canon:canon-slides-draft` | For a deck, drafted as `.claude/SLIDES.md` and rendered to PowerPoint |
300
+ | `canon:canon-screencast` | For a recording script with beats and defaults already seeded |
299
301
 
300
302
  ### Answer a question at any point
301
303
 
302
304
  | Skill | When to use |
303
305
  | --------------------------- | ------------------------------------------------------------------------------ |
304
306
  | `canon:canon-cli` | Before a sync or install, to learn what it overwrites, merges, or leaves alone |
307
+ | `canon:index-lookup` | To find where a topic is documented across the tracked `index.md` catalogs |
305
308
  | `canon:youtube-transcripts` | When a video transcript is wanted in the repo as context |
306
309
  | `canon:claude-teach` | To learn a subject across sessions, in a workspace that holds the progress |
307
310
  | `canon:write-human` | Before drafting or revising prose, for voice, rhythm, and density |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "4.54.0",
4
+ "version": "4.56.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {