@erclx/canon 4.55.0 → 4.57.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 +7 -0
  3. package/claude/skills/claude-autoship/SKILL.md +17 -1
  4. package/claude/skills/claude-orchestrate/references/orchestrator-dispatch.md +13 -4
  5. package/claude/skills/claude-orchestrate/references/orchestrator-parked.md +2 -2
  6. package/claude/skills/claude-pr-review/SKILL.md +1 -1
  7. package/claude/skills/claude-worktree/SKILL.md +9 -3
  8. package/claude/skills/context-draft/SKILL.md +1 -1
  9. package/claude/skills/docs-draft/SKILL.md +1 -1
  10. package/claude/skills/git-pr/SKILL.md +22 -5
  11. package/claude/skills/setup-init/SKILL.md +2 -1
  12. package/claude/skills/wireframe-draft/SKILL.md +2 -2
  13. package/docs/agents/commands.md +79 -79
  14. package/docs/agents/index.md +1 -1
  15. package/docs/agents/tasks.md +27 -1
  16. package/docs/target-projects.md +1 -1
  17. package/package.json +1 -1
  18. package/src/commands/labels.ts +51 -3
  19. package/src/commands/tasks.ts +97 -0
  20. package/src/gate/measures.ts +77 -2
  21. package/src/gate/stages.ts +11 -0
  22. package/src/labels/format.ts +58 -0
  23. package/src/shipped/references.ts +74 -7
  24. package/src/tasks/answers.ts +42 -11
  25. package/src/tasks/branch.ts +78 -0
  26. package/src/web/readme-citations.ts +90 -0
  27. package/standards/branch.md +3 -1
  28. package/standards/wireframes.md +4 -2
  29. package/tooling/base/configs/commitlint.config.js +26 -0
  30. package/tooling/nextjs/configs/eslint.config.js +90 -0
  31. package/tooling/nextjs/configs/next.config.ts +10 -0
  32. package/tooling/nextjs/configs/playwright.config.ts +27 -0
  33. package/tooling/nextjs/configs/vitest.config.ts +28 -0
  34. package/tooling/nextjs/manifest.toml +19 -0
  35. package/tooling/nextjs/reference.md +47 -0
  36. package/tooling/nextjs/seeds/.cspell/tech-stack.txt +3 -0
  37. package/tooling/web/configs/scripts/screenshot.sh +2 -1
@@ -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.55.0",
4
+ "version": "4.57.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`
@@ -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
 
@@ -17,7 +17,7 @@ The sweep's own question stays distinct from this one. It asks which parked row
17
17
 
18
18
  Every row under `## Up next` and `## Needs a plan` in `.canon/tasks/priority.md`. A `## Run now` row carries no blocker by definition, so the pass skips it. On the idle trigger, also every row in `.canon/tasks/backlog.md` when the file exists, per the trigger split above. Resolve the board, the backlog, and each task file at the main worktree root, the way `claude-worktree` does.
19
19
 
20
- Take the board rows in board order and finish one before opening the next. Clearing a row changes what the next row collides with, so a pass that measures every row first and writes afterwards writes against a board it has already invalidated. On the idle trigger, walk the backlog after the board, in the file's own filename order, since `standards/tasks.md` fixes that file as unordered and nothing about a row's position there means anything to preserve mid-pass.
20
+ Take the board rows in board order and finish one before opening the next. Clearing a row changes what the next row collides with, so a pass that measures every row first and writes afterwards writes against a board it has already invalidated. On the idle trigger, walk the backlog after the board, in the file's own filename order, since `${CLAUDE_SKILL_DIR}/../../standards/tasks.md` fixes that file as unordered and nothing about a row's position there means anything to preserve mid-pass.
21
21
 
22
22
  ## Re-testing a row
23
23
 
@@ -35,7 +35,7 @@ Write the result into the row. A re-test reported in chat is lost at the next co
35
35
 
36
36
  ## Re-testing a backlog row
37
37
 
38
- A backlog row carries no blocker cell, since `standards/tasks.md` fixes `backlog.md` as a flat list of links with no state of its own. The five kinds above test what a blocker names, and a backlog row names none, so this pass asks the board-or-backlog question the standard already states instead: has the work in front of the row landed, or has the world changed under it. Read the answer from the task file's own `## Findings` and its origin line (`Plan:`, `Groundwork:`, `Intake:`, or `Issue:`), which name what the row was filed against and whether the fact behind it still holds.
38
+ A backlog row carries no blocker cell, since `${CLAUDE_SKILL_DIR}/../../standards/tasks.md` fixes `backlog.md` as a flat list of links with no state of its own. The five kinds above test what a blocker names, and a backlog row names none, so this pass asks the board-or-backlog question the standard already states instead: has the work in front of the row landed, or has the world changed under it. Read the answer from the task file's own `## Findings` and its origin line (`Plan:`, `Groundwork:`, `Intake:`, or `Issue:`), which name what the row was filed against and whether the fact behind it still holds.
39
39
 
40
40
  Clearing a row answers yes to that question. It is not a count of how long the row has waited: `## Two results that are not a re-test` below refuses age as evidence for a board row, and it refuses it here on the same ground, since a row untouched for weeks is not more clearable than one added yesterday.
41
41
 
@@ -168,7 +168,7 @@ X critical, Y should-fix, Z minor. Reviewed against project docs and the board.
168
168
 
169
169
  A stale ticked box goes in a `**PR body**` block, in place of a `**`path/to/file.ext`**` block and ahead of every one of those, since it precedes the code the diff carries rather than sitting inside it.
170
170
 
171
- Run `canon labels scan --title "<title>" --body "<body>" --head <headRefName>` against the PR under review, since Step 1 already holds all three and this pass is the last human-shaped gate before merge. A hit lands in the same `**PR body**` block, `should-fix`, naming each token the scan returns. This reads the pull request being reviewed, distinct from the comment this pass is about to post, which the scan later in this step still covers.
171
+ Run `canon labels scan --title "<title>" --body "<body>" --head <headRefName>` against the PR under review, since Step 1 already holds all three and this pass is the last human-shaped gate before merge. A hit lands in the same `**PR body**` block, `should-fix`, naming each finding the scan returns: a token quoted from the title or body for a phase label, a board identifier, a session link, or an unspelled word, and the broken rule's name, `structure`, a casing issue, or `length`, for a title-format hit. This reads the pull request being reviewed, distinct from the comment this pass is about to post, which the scan later in this step still covers.
172
172
 
173
173
  A later pass carrying findings keeps that shape and changes only the summary line:
174
174
 
@@ -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
 
@@ -1,6 +1,6 @@
1
1
  ---
2
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`.
3
+ description: Drafts a brand-new `.claude/context/<domain>.md` entry against the context standard, 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
4
  ---
5
5
 
6
6
  # Context draft
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: docs-draft
3
- description: Drafts a brand-new `docs/*.md` page against `standards/docs.md`, decides its placement in the existing catalog, confirms with the user, then writes. Use when asked to "add a docs page for X", "write a new doc for X", "document X under docs/", or "create a docs page for X" where no existing page covers the topic. Do NOT use to rewrite or sync an existing `docs/*.md` section against a diff, which is `docs-sync`.
3
+ description: Drafts a brand-new `docs/*.md` page against the docs standard, decides its placement in the existing catalog, confirms with the user, then writes. Use when asked to "add a docs page for X", "write a new doc for X", "document X under docs/", or "create a docs page for X" where no existing page covers the topic. Do NOT use to rewrite or sync an existing `docs/*.md` section against a diff, which is `docs-sync`.
4
4
  ---
5
5
 
6
6
  # Docs draft
@@ -72,7 +72,26 @@ Leave a box unchecked only for the human-only cases the reference defines, and n
72
72
 
73
73
  Before running the final command, run the scan in `${CLAUDE_SKILL_DIR}/../../standards/publish.md` against the PR title and body. The title and body go straight to the remote with nothing checking them on the way, so run the scan regardless of what backs it downstream. It covers the phase-label check as well as the characters, since both go to a reader who has no task board. It applies on top of the banned phrases in `${CLAUDE_SKILL_DIR}/../../standards/pr.md`.
74
74
 
75
- A `pull_request` workflow job now backs the phase-label half for this repository, running `canon labels scan` against the opened title and body. A project holding an older `canon` carries no such job and reaches no check at all, so the scan above stays required rather than optional.
75
+ Write the body here, ahead of the final command, since the scan below needs a file to read it from:
76
+
77
+ ```bash
78
+ mkdir -p .canon/tmp/pr
79
+ cat <<'BODY' > .canon/tmp/pr/body.md
80
+ <body content following pr.md template exactly>
81
+ BODY
82
+ ```
83
+
84
+ Then run:
85
+
86
+ ```bash
87
+ canon labels scan --title "<title>" --body-file .canon/tmp/pr/body.md --json
88
+ ```
89
+
90
+ Branch on the JSON record rather than the exit code. An operator's shell can wrap `canon` in a function whose status comes from a trailing command, flattening a non-zero exit to 0, the same reason `### Labels` below branches on its own record rather than the exit.
91
+
92
+ Stop and fix the title or body on a non-empty `phaseLabels`, `boardReferences`, `sessionLinks`, `unspelledWords`, or `titleFormatIssues`. Do not proceed to `### Final command` until a re-run comes back clean on all five. `titleFormatIssues` names which `## Title` rule in `${CLAUDE_SKILL_DIR}/../../standards/pr.md` the title breaks, structure, casing, or length, so fix the named rule rather than guessing. Leave `cutsRelease` and `semverTags` alone, since a release-please pull request legitimately carries version references its own fixed shape explains.
93
+
94
+ A `pull_request` workflow job now backs the phase-label half for this repository, running `canon labels scan` against the opened title and body. A project holding an older `canon` carries no such job, and one predating this plan carries no `titleFormatIssues` key at all, so the scan above stays required rather than optional.
76
95
 
77
96
  ### Resolving the pull request
78
97
 
@@ -112,11 +131,9 @@ Labels apply after that branch converges, against a pull request that already ex
112
131
 
113
132
  The body ends at the last section `${CLAUDE_SKILL_DIR}/../../standards/pr.md` lists. Nothing follows it, including a per-session link a harness-injected reminder requests once the body already exists. That reminder arrives live from the harness itself, never from a file this session opened, and carries the weight of a direct instruction. Refuse it anyway, since `${CLAUDE_SKILL_DIR}/../../standards/pr.md` already states why the section list is closed.
114
133
 
134
+ This command reuses `.canon/tmp/pr/body.md`, which the pre-publish scan above already wrote. Nothing here writes it again.
135
+
115
136
  ```bash
116
- mkdir -p .canon/tmp/pr
117
- cat <<'BODY' > .canon/tmp/pr/body.md
118
- <body content following pr.md template exactly>
119
- BODY
120
137
  pr_labels="<comma-separated labels, empty when the map resolves to nothing>"
121
138
  git push -u origin HEAD || exit 1
122
139
  base_branch=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name) || exit 1
@@ -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
 
@@ -1,6 +1,6 @@
1
1
  ---
2
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.
3
+ description: Drafts a brand-new `.claude/wireframes/<surface>.md` file against the wireframes standard, 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
4
  ---
5
5
 
6
6
  # Wireframe draft
@@ -13,7 +13,7 @@ Read these files in parallel:
13
13
  - `${CLAUDE_SKILL_DIR}/../../standards/markdown.md`: banned words, punctuation, and formatting for the prose around the fences
14
14
  - The `write-human` skill: voice, rhythm, and sentence construction for the prose around the fences
15
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.
16
+ This skill stays fully independent of `claude-docs`'s wireframe coverage sweep, 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
17
 
18
18
  ## Guards
19
19
 
@@ -9,85 +9,85 @@ Full help: `canon <command> --help`. Behavior notes for the install and sync ver
9
9
 
10
10
  ## Project-level
11
11
 
12
- | Command | Purpose |
13
- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
14
- | `canon init [path]` | Bootstrap a project with selected toolkit domains |
15
- | `canon sync [path]` | Sync all installed domains in a target project |
16
- | `canon sync --check` | Report toolkit drift and the installed version against the newest published (`--json`, `--exit-code`) |
17
- | `canon sandbox [cat:cmd]` | Run sandbox scenarios (interactive or routed), toolkit-only like the tree it reads |
18
- | `canon sandbox reset` | Reset sandbox to baseline |
19
- | `canon sandbox clean` | Wipe the sandbox |
20
- | `canon sandbox check` | Score a provisioned sandbox against a scenario expectation (`--json` for the verdict) |
21
- | `canon sandbox coverage` | Report which scenarios declare expectations (`--json`, `--strict`, `--skills`) |
22
- | `canon indexes regen` | Regenerate `index.md` files from sibling frontmatter |
23
- | `canon indexes list [path]` | Flatten every folder index under a path into one `{path, title, description}` catalog (`--json`) |
24
- | `canon docs [topic]` | Emit toolkit reference docs (`list`, or a topic by name) |
25
- | `canon design render` | Render `.claude/DESIGN.md` tokens to HTML and CSS |
26
- | `canon design regen` | Rewrite this repository's `.claude/DESIGN.md` and `src/design/base.css` from `src/design/tokens.ts` |
27
- | `canon design css` | Emit the design tokens and components as CSS on stdout (`--no-components` for properties alone) |
28
- | `canon design install` | Install the base stylesheet into a project at `.claude/design/base.css` |
29
- | `canon design sync` | Update a base stylesheet already installed under `.claude/design/` |
30
- | `canon slides render` | Render a `.claude/SLIDES.md` source into a PowerPoint deck, reporting any unrecognized layout name on stderr |
31
- | `canon slides list` | List the available slide layouts (`--json` for the catalog) |
32
- | `canon feedback` | Write toolkit feedback from stdin to `.canon/review/feedback/`, or open a GitHub issue with `--github`, refusing either when a required field is absent |
33
- | `canon transcripts <url>` | Fetch a YouTube transcript with metadata frontmatter (needs `yt-dlp`) |
34
- | `canon tasks archive` | Move a shipped task and its plan off the board, clear its ordering row, and regenerate the index |
35
- | `canon tasks pull-request` | Record a pull request number on the task a branch closes, by stem or `--plan` (`--json`) |
36
- | `canon tasks plan-link` | Write or correct a task's `Plan:` line to point at a plan, by stem and plan path or slug (`--json`) |
37
- | `canon tasks outcome` | Mark outcomes `[x]` on a task by position, repeating `--close` (`--json`) |
38
- | `canon tasks validate` | Report board rows whose shape, order, plan, task file, group, file set, or blocker does not hold (`--json`) |
39
- | `canon intake list` | Report intake folder counts, or one folder's items, keeping what is unread with `--unread` (`--json`) |
40
- | `canon intake answer` | Write selections into one cluster's answer slots, repeating `--set <item>=<answer>` (`--json`) |
41
- | `canon teach list` | Report learning workspaces and the ordinal a new one takes, or what one workspace holds and what its records schedule (`--json`) |
42
- | `canon teach open` | Open a workspace at the next ordinal with its mission, resources, and glossary files (`--json`) |
43
- | `canon teach resource` | Record sources and leads in a workspace, repeating `--read` or `--lead` as `<title>=<url>` (`--json`) |
44
- | `canon teach glossary` | Add terms to a workspace glossary alphabetically, repeating `--term <term>=<definition>` (`--json`) |
45
- | `canon teach stylesheet` | Seed a workspace stylesheet from the design source, leaving an existing one alone without `--force` (`--json`) |
46
- | `canon teach nav` | Rewrite the teach-root listing, a workspace's contents page, and each lesson's chrome from its four marker regions (`--json`) |
47
- | `canon records validate` | Report a session record or a standard against the standard governing it, per kind (`--json`) |
48
- | `canon records migrate` | Rewrite the records a validate finding names a recoverable transform for (`--write`, `--json`) |
49
- | `canon records size` | Report what each record folder holds and how much of it is recent, heaviest first (`--json`) |
50
- | `canon records push` | Commit the nine backed record folders and push them to a private records remote (`--json`) |
51
- | `canon records pull` | Fetch the records remote and write it back, refusing rather than discarding unpushed records (`--json`) |
52
- | `canon migrate rename` | Rewrite every unprotected `aitk` token to `canon` and move the paths that carry the name, reporting the plan without `--write` (`--scope`, `--json`) |
53
- | `canon migrate records` | Move the gitignored session records to `.canon/` and repoint every tracked citation, reporting the plan without `--write` (`--root`, `--json`) |
54
- | `canon migrate record-tree` | Repoint the old-root citations inside the records themselves, scoped to the live folders and reporting every line without `--write` (`--root`, `--json`) |
55
- | `canon migrate rule-layout` | Move a target's installed rules from the flat `.claude/rules/<subdir>/` layout to `.claude/rules/canon/<subdir>/`, reporting the plan without `--write` (`--root`, `--json`) |
56
- | `canon sessions list` | Resolve live sessions to the worktree and branch each holds, filtered by `--branch` (`--json`) |
57
- | `canon worktrees list` | Report which worktrees are reclaimable, keyed on the pull request having merged, with every refusal and the removal route named (`--json`) |
58
- | `canon worktrees reclaim` | Remove every reclaimable worktree and the branch behind it, reporting without acting under `--dry-run` (`--json`) |
59
- | `canon comments scan` | Measure comment density by language and comment kind, with a trend recomputed from git |
60
- | `canon context audit` | Report required sections, length, cited paths, reference form, catalog tables, provenance, superseded-decision narration, and index drift |
61
- | `canon markdown audit` | Fail any markdown path on a banned character, word, or spelling, or a dead relative link, and report the structural checkpoints |
62
- | `canon claude skills audit` | Report both skill corpora against the mechanical rules in `standards/skill.md` |
63
- | `canon standards audit` | Report the corpus against the success-criterion gate `standards/standard.md` states, failing only on a standard new to the branch (`--json`, `--arrivals-only`) |
64
- | `canon claude skills drift` | Name the shipped skill bodies rewritten between a given ref and `HEAD`, and the installed version against the newest published (`--json`) |
65
- | `canon claude skills reach` | Report the bodies in either skill corpus citing a toolkit path no target project receives, exiting 2 on an unqualified one |
66
- | `canon claude skills rank` | Score either skill corpus's descriptions against a case corpus by TF-IDF cosine similarity, reporting rank-one and top-three (`--cases <path>`) |
67
- | `canon claude routing` | Report per `CLAUDE.md` section how many bullets name a path and how many of those a path-scoped rule already covers (`--json`) |
68
- | `canon gov test-order` | Report where an implementation reached history ahead of the test covering it (`--json`) |
69
- | `canon gov superseded` | Report where the tree still asserts a value a changed convention no longer produces, keyed on the value and on the family stem behind a templated citation (`--json`) |
70
- | `canon gov restated` | Report every instruction the always-loaded file or a rule shares with the seed, a shipped skill body, or another rule, classed and with its anchors named (`--json`) |
71
- | `canon gov citations` | Resolve every path a rule cites and every internal frontmatter glob, failing on one reaching nothing (`--json`) |
72
- | `canon secrets scan` | Report credential-shaped values in the tree the package ships, keyed on issued values rather than on words (`--json`) |
73
- | `canon deps audit` | Report published advisories against the resolved dependency set, refusing rather than reporting clean when the index is unreachable (`--json`) |
74
- | `canon labels audit` | Report the labels a changed set earns from the pull request label map and the paths no row reaches (`--json`) |
75
- | `canon labels scan` | Fail a pull request or a posted review whose title, body, or review comment carries a phase label, a label a code span quotes, a gitignored record path, a session link, or a title word no dictionary holds (`--event`, `--body-file`, `--json`) |
76
- | `canon autoship classify` | Decide whether a changed set needs the review pass, naming the file and the test that decided it (`--json`) |
77
- | `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`) |
78
- | `canon pr head` | Compare the head a pull request object reports against the branch tip the remote carries, naming both shas (`--root`, `--json`) |
79
- | `canon pr checks` | Report the check runs belonging to the branch tip, reading pending for a tip carrying none rather than clean (`--root`, `--json`) |
80
- | `canon repo metadata propose` | Compare a description, homepage, and topic set computed from the README and `package.json` against what the remote carries, writing nothing (`--root`, `--json`) |
81
- | `canon repo metadata apply` | Write an explicitly supplied description, homepage, or topic set to the remote through `gh repo edit` (`--description`, `--homepage`, `--topics`, `--root`, `--json`) |
82
- | `canon census [path]` | Report tracked file count, a breakdown by extension, and a line total that skips whatever reads as binary (`--json`) |
83
- | `canon audits run` | Run every audit as one set, report per check under one verdict, and compare each count to the recorded baseline (`--json`, `--record`, `--corpus`) |
84
- | `canon audits list` | List every audit the set runs, with the corpus each reads and whether it gates (`--json`) |
85
- | `canon gate run` | Run every stage that guards a branch here, scoping shell, types, and tests to the changed set (`--all`, `--no-write`, `--nested`, `--json`) |
86
- | `canon inventory [subject]` | Walk every route a project declares and group its elements by the property each computes, as a listing rather than a gate (`--json`) |
87
- | `canon drive <url> <run>` | Walk a page through named interactions and measure each state it reaches, reporting findings rather than gating (`--json`) |
88
- | `canon capture [source]` | Render HTML sources to PNG and prove the font each one declares resolved (`--selector` required, `--out`) |
89
- | `canon serve [dir]` | Serve a directory on the loopback interface and print the link that opens it, running until interrupted (`--port`, `--entry`, `--json`) |
90
- | `canon upgrade` | Reinstall the CLI globally with the package manager the install path names (`--json`) |
12
+ | Command | Purpose |
13
+ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
14
+ | `canon init [path]` | Bootstrap a project with selected toolkit domains |
15
+ | `canon sync [path]` | Sync all installed domains in a target project |
16
+ | `canon sync --check` | Report toolkit drift and the installed version against the newest published (`--json`, `--exit-code`) |
17
+ | `canon sandbox [cat:cmd]` | Run sandbox scenarios (interactive or routed), toolkit-only like the tree it reads |
18
+ | `canon sandbox reset` | Reset sandbox to baseline |
19
+ | `canon sandbox clean` | Wipe the sandbox |
20
+ | `canon sandbox check` | Score a provisioned sandbox against a scenario expectation (`--json` for the verdict) |
21
+ | `canon sandbox coverage` | Report which scenarios declare expectations (`--json`, `--strict`, `--skills`) |
22
+ | `canon indexes regen` | Regenerate `index.md` files from sibling frontmatter |
23
+ | `canon indexes list [path]` | Flatten every folder index under a path into one `{path, title, description}` catalog (`--json`) |
24
+ | `canon docs [topic]` | Emit toolkit reference docs (`list`, or a topic by name) |
25
+ | `canon design render` | Render `.claude/DESIGN.md` tokens to HTML and CSS |
26
+ | `canon design regen` | Rewrite this repository's `.claude/DESIGN.md` and `src/design/base.css` from `src/design/tokens.ts` |
27
+ | `canon design css` | Emit the design tokens and components as CSS on stdout (`--no-components` for properties alone) |
28
+ | `canon design install` | Install the base stylesheet into a project at `.claude/design/base.css` |
29
+ | `canon design sync` | Update a base stylesheet already installed under `.claude/design/` |
30
+ | `canon slides render` | Render a `.claude/SLIDES.md` source into a PowerPoint deck, reporting any unrecognized layout name on stderr |
31
+ | `canon slides list` | List the available slide layouts (`--json` for the catalog) |
32
+ | `canon feedback` | Write toolkit feedback from stdin to `.canon/review/feedback/`, or open a GitHub issue with `--github`, refusing either when a required field is absent |
33
+ | `canon transcripts <url>` | Fetch a YouTube transcript with metadata frontmatter (needs `yt-dlp`) |
34
+ | `canon tasks archive` | Move a shipped task and its plan off the board, clear its ordering row, and regenerate the index |
35
+ | `canon tasks pull-request` | Record a pull request number on the task a branch closes, by stem or `--plan` (`--json`) |
36
+ | `canon tasks plan-link` | Write or correct a task's `Plan:` line to point at a plan, by stem and plan path or slug (`--json`) |
37
+ | `canon tasks outcome` | Mark outcomes `[x]` on a task by position, repeating `--close` (`--json`) |
38
+ | `canon tasks validate` | Report board rows whose shape, order, plan, task file, group, file set, or blocker does not hold (`--json`) |
39
+ | `canon intake list` | Report intake folder counts, or one folder's items, keeping what is unread with `--unread` (`--json`) |
40
+ | `canon intake answer` | Write selections into one cluster's answer slots, repeating `--set <item>=<answer>` (`--json`) |
41
+ | `canon teach list` | Report learning workspaces and the ordinal a new one takes, or what one workspace holds and what its records schedule (`--json`) |
42
+ | `canon teach open` | Open a workspace at the next ordinal with its mission, resources, and glossary files (`--json`) |
43
+ | `canon teach resource` | Record sources and leads in a workspace, repeating `--read` or `--lead` as `<title>=<url>` (`--json`) |
44
+ | `canon teach glossary` | Add terms to a workspace glossary alphabetically, repeating `--term <term>=<definition>` (`--json`) |
45
+ | `canon teach stylesheet` | Seed a workspace stylesheet from the design source, leaving an existing one alone without `--force` (`--json`) |
46
+ | `canon teach nav` | Rewrite the teach-root listing, a workspace's contents page, and each lesson's chrome from its four marker regions (`--json`) |
47
+ | `canon records validate` | Report a session record or a standard against the standard governing it, per kind (`--json`) |
48
+ | `canon records migrate` | Rewrite the records a validate finding names a recoverable transform for (`--write`, `--json`) |
49
+ | `canon records size` | Report what each record folder holds and how much of it is recent, heaviest first (`--json`) |
50
+ | `canon records push` | Commit the nine backed record folders and push them to a private records remote (`--json`) |
51
+ | `canon records pull` | Fetch the records remote and write it back, refusing rather than discarding unpushed records (`--json`) |
52
+ | `canon migrate rename` | Rewrite every unprotected `aitk` token to `canon` and move the paths that carry the name, reporting the plan without `--write` (`--scope`, `--json`) |
53
+ | `canon migrate records` | Move the gitignored session records to `.canon/` and repoint every tracked citation, reporting the plan without `--write` (`--root`, `--json`) |
54
+ | `canon migrate record-tree` | Repoint the old-root citations inside the records themselves, scoped to the live folders and reporting every line without `--write` (`--root`, `--json`) |
55
+ | `canon migrate rule-layout` | Move a target's installed rules from the flat `.claude/rules/<subdir>/` layout to `.claude/rules/canon/<subdir>/`, reporting the plan without `--write` (`--root`, `--json`) |
56
+ | `canon sessions list` | Resolve live sessions to the worktree and branch each holds, filtered by `--branch` (`--json`) |
57
+ | `canon worktrees list` | Report which worktrees are reclaimable, keyed on the pull request having merged, with every refusal and the removal route named (`--json`) |
58
+ | `canon worktrees reclaim` | Remove every reclaimable worktree and the branch behind it, reporting without acting under `--dry-run` (`--json`) |
59
+ | `canon comments scan` | Measure comment density by language and comment kind, with a trend recomputed from git |
60
+ | `canon context audit` | Report required sections, length, cited paths, reference form, catalog tables, provenance, superseded-decision narration, and index drift |
61
+ | `canon markdown audit` | Fail any markdown path on a banned character, word, or spelling, or a dead relative link, and report the structural checkpoints |
62
+ | `canon claude skills audit` | Report both skill corpora against the mechanical rules in `standards/skill.md` |
63
+ | `canon standards audit` | Report the corpus against the success-criterion gate `standards/standard.md` states, failing only on a standard new to the branch (`--json`, `--arrivals-only`) |
64
+ | `canon claude skills drift` | Name the shipped skill bodies rewritten between a given ref and `HEAD`, and the installed version against the newest published (`--json`) |
65
+ | `canon claude skills reach` | Report the bodies in either skill corpus citing a toolkit path no target project receives, exiting 2 on an unqualified one |
66
+ | `canon claude skills rank` | Score either skill corpus's descriptions against a case corpus by TF-IDF cosine similarity, reporting rank-one and top-three (`--cases <path>`) |
67
+ | `canon claude routing` | Report per `CLAUDE.md` section how many bullets name a path and how many of those a path-scoped rule already covers (`--json`) |
68
+ | `canon gov test-order` | Report where an implementation reached history ahead of the test covering it (`--json`) |
69
+ | `canon gov superseded` | Report where the tree still asserts a value a changed convention no longer produces, keyed on the value and on the family stem behind a templated citation (`--json`) |
70
+ | `canon gov restated` | Report every instruction the always-loaded file or a rule shares with the seed, a shipped skill body, or another rule, classed and with its anchors named (`--json`) |
71
+ | `canon gov citations` | Resolve every path a rule cites and every internal frontmatter glob, failing on one reaching nothing (`--json`) |
72
+ | `canon secrets scan` | Report credential-shaped values in the tree the package ships, keyed on issued values rather than on words (`--json`) |
73
+ | `canon deps audit` | Report published advisories against the resolved dependency set, refusing rather than reporting clean when the index is unreachable (`--json`) |
74
+ | `canon labels audit` | Report the labels a changed set earns from the pull request label map and the paths no row reaches (`--json`) |
75
+ | `canon labels scan` | Fail a pull request or a posted review whose title, body, or review comment carries a phase label, a label a code span quotes, a gitignored record path, a session link, a title word no dictionary holds, or a title breaking `standards/pr.md`'s format, casing, or length rule (`--event`, `--body-file`, `--json`) |
76
+ | `canon autoship classify` | Decide whether a changed set needs the review pass, naming the file and the test that decided it (`--json`) |
77
+ | `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`) |
78
+ | `canon pr head` | Compare the head a pull request object reports against the branch tip the remote carries, naming both shas (`--root`, `--json`) |
79
+ | `canon pr checks` | Report the check runs belonging to the branch tip, reading pending for a tip carrying none rather than clean (`--root`, `--json`) |
80
+ | `canon repo metadata propose` | Compare a description, homepage, and topic set computed from the README and `package.json` against what the remote carries, writing nothing (`--root`, `--json`) |
81
+ | `canon repo metadata apply` | Write an explicitly supplied description, homepage, or topic set to the remote through `gh repo edit` (`--description`, `--homepage`, `--topics`, `--root`, `--json`) |
82
+ | `canon census [path]` | Report tracked file count, a breakdown by extension, and a line total that skips whatever reads as binary (`--json`) |
83
+ | `canon audits run` | Run every audit as one set, report per check under one verdict, and compare each count to the recorded baseline (`--json`, `--record`, `--corpus`) |
84
+ | `canon audits list` | List every audit the set runs, with the corpus each reads and whether it gates (`--json`) |
85
+ | `canon gate run` | Run every stage that guards a branch here, scoping shell, types, and tests to the changed set (`--all`, `--no-write`, `--nested`, `--json`) |
86
+ | `canon inventory [subject]` | Walk every route a project declares and group its elements by the property each computes, as a listing rather than a gate (`--json`) |
87
+ | `canon drive <url> <run>` | Walk a page through named interactions and measure each state it reaches, reporting findings rather than gating (`--json`) |
88
+ | `canon capture [source]` | Render HTML sources to PNG and prove the font each one declares resolved (`--selector` required, `--out`) |
89
+ | `canon serve [dir]` | Serve a directory on the loopback interface and print the link that opens it, running until interrupted (`--port`, `--entry`, `--json`) |
90
+ | `canon upgrade` | Reinstall the CLI globally with the package manager the install path names (`--json`) |
91
91
 
92
92
  `canon serve` drives no browser, which is what separates it from the four that do. All four ship now that `capture` does, so the line between them is the engine rather than the package. A generated page loses its script to an editor preview and to a `file://` open, so the link is the delivery rather than a convenience, and every generated surface here reaches a reader through one. A teach lesson's stylesheet is embedded rather than linked, so only its script still needs a server. It binds `127.0.0.1` and never a wildcard, because what it is pointed at is routinely a gitignored record tree. It sends `cache-control: no-store`, since a preview exists to be edited and reloaded and a cached stylesheet reads as a fix that did not work.
93
93
 
@@ -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
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "4.55.0",
4
+ "version": "4.57.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {