@erclx/canon 4.8.0 → 4.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -41
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/claude-docs/REQUIREMENT.md +2 -2
- package/claude/skills/claude-docs/SKILL.md +7 -36
- package/claude/skills/claude-orchestrate/references/orchestrator-dispatch.md +13 -8
- package/claude/skills/claude-tasks/SKILL.md +4 -5
- package/claude/skills/claude-teach/SKILL.md +11 -1
- package/claude/skills/claude-worker/SKILL.md +2 -2
- package/docs/agents/commands.md +8 -3
- package/docs/agents/install-and-sync.md +23 -1
- package/docs/agents/overview.md +3 -3
- package/docs/agents/tasks.md +10 -4
- package/docs/agents/teach.md +2 -0
- package/docs/ai-workflow.md +2 -4
- package/docs/visual-design-workflow.md +2 -0
- package/docs/zshrc-aliases.md +19 -7
- package/package.json +1 -1
- package/scripts/core/bootstrap.sh +4 -0
- package/src/commands/design.ts +147 -5
- package/src/commands/sync.ts +3 -5
- package/src/commands/tasks.ts +18 -1
- package/src/commands/teach.ts +72 -0
- package/src/design/adapter.ts +59 -0
- package/src/design/base.css +123 -0
- package/src/design/components.ts +118 -0
- package/src/design/contrast.ts +81 -0
- package/src/design/css.ts +142 -0
- package/src/design/document.ts +143 -0
- package/src/design/regen.ts +56 -0
- package/src/design/render.ts +43 -9
- package/src/design/tokens.ts +315 -0
- package/src/gate/stages.ts +28 -0
- package/src/slides/styles.ts +42 -15
- package/src/sync/check.ts +41 -0
- package/src/sync/engine.ts +28 -7
- package/src/sync/stamp.ts +5 -4
- package/src/sync/target.ts +4 -1
- package/src/tasks/archive.ts +91 -20
- package/src/tasks/validate.ts +76 -0
- package/src/teach/workspace.ts +57 -0
- package/standards/tasks.md +4 -4
package/README.md
CHANGED
|
@@ -4,11 +4,23 @@
|
|
|
4
4
|
[](https://github.com/erclx/canon/actions/workflows/verify.yml)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
canon is a CLI and Claude Code plugin that stops your AI conventions drifting apart across repositories. It keeps one authoritative copy and installs it into each project on demand.
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
The counts above are read from the catalogs when the image is built, so they're what the repo actually ships today.
|
|
12
|
+
|
|
13
|
+
## Why
|
|
14
|
+
|
|
15
|
+
If you work across more than one repository and your AI setup has started to drift between them, this is for you. Every AI coding setup accumulates the same assets. Prompts to reuse, rules agents should follow, slash commands, skills, seed docs, sync scripts. Once you have enough projects, your copies drift and your agents stop getting consistent signals.
|
|
16
|
+
|
|
17
|
+
Three design choices shape the toolkit.
|
|
18
|
+
|
|
19
|
+
- Agent-first: every command has a non-interactive path and a JSON catalog. If a Claude Code skill or any other agent cannot drive the CLI without prompts, the design is wrong.
|
|
20
|
+
- Text-native: conventions, rules, and prompts are authored as markdown that you and your agents read the same way. No hidden behavior, no compiled state.
|
|
21
|
+
- One source, many consumers: this repo is the authoritative copy. Your projects install and sync on demand, never author in place.
|
|
22
|
+
|
|
23
|
+
Two limits worth knowing before you install. Claude Code is the only agent runtime the plugin targets, and the CLI needs Bun on your path.
|
|
12
24
|
|
|
13
25
|
## Install
|
|
14
26
|
|
|
@@ -31,6 +43,31 @@ bun install --global @erclx/canon
|
|
|
31
43
|
|
|
32
44
|
[Bun](https://bun.sh) is the CLI runtime and has to be on your path first. Confirm the install by resolving `canon --help`.
|
|
33
45
|
|
|
46
|
+
## What is inside
|
|
47
|
+
|
|
48
|
+
Each domain has a canonical source in this repo and a thin install or sync CLI on your side.
|
|
49
|
+
|
|
50
|
+
- Claude Code plugin: skills that plan a feature, review a diff, sync the planning docs, and run the ship chain from branch through pull request
|
|
51
|
+
- Governance rules: coding and authoring rules that load into a Claude session when a matching path is edited, installed per project and refreshed by sync
|
|
52
|
+
- Standards: authoring conventions for commits, branches, plans, tasks, and markdown, read by name with `canon standards <name>` rather than copied into your project
|
|
53
|
+
- Snippets: reusable prompts fired by `@` reference in a Claude Code session, resolved live from the plugin with no install step
|
|
54
|
+
- Tooling stacks: golden configs, seeds, and a reference per framework, laid down by `canon init` and reconciled by `canon tooling sync`
|
|
55
|
+
- Design system: a `DESIGN.md` token format, a skill that drafts one from an existing project or from scratch, and a render command
|
|
56
|
+
- Slides: a `SLIDES.md` source format with a layout catalog, rendered to PowerPoint by `canon slides render`
|
|
57
|
+
- Transcripts: `canon transcripts` pulls a YouTube transcript with metadata frontmatter into any repo
|
|
58
|
+
- Sandbox: scenario-based scaffolds that provision an isolated project state for verifying each domain flow
|
|
59
|
+
|
|
60
|
+
## Documentation
|
|
61
|
+
|
|
62
|
+
Scaffolding your first project? Start with target projects, then the AI workflow loop. Everything else answers questions that arrive later.
|
|
63
|
+
|
|
64
|
+
- [AI workflow](docs/ai-workflow.md): feature-development loop inside a toolkit-managed project
|
|
65
|
+
- [Operating model](docs/operating-model.md): orchestrator, planner, and worker roles for building across parallel sessions
|
|
66
|
+
- [Visual design workflow](docs/visual-design-workflow.md): tiered guide for design and wireframe authoring
|
|
67
|
+
- [Target projects](docs/target-projects.md): scaffold, add a domain later, sync upstream drift
|
|
68
|
+
- [Agents](docs/agents/index.md): CLI flags, exit codes, and JSON output shapes
|
|
69
|
+
- [Docs index](docs/index.md): every reference doc in this repo
|
|
70
|
+
|
|
34
71
|
## Update
|
|
35
72
|
|
|
36
73
|
Nothing refreshes on its own. Claude Code ships auto-update off for third-party marketplaces, so an installed copy serves whatever version it was installed at until you refresh it.
|
|
@@ -49,43 +86,6 @@ You don't have to wait until something breaks to find out you're behind. `canon
|
|
|
49
86
|
|
|
50
87
|
To stop doing this by hand, turn auto-update on once under `/plugin` in the Marketplaces tab. Confirm what you are running with `canon --version` and `claude plugin list`.
|
|
51
88
|
|
|
52
|
-
## Why
|
|
53
|
-
|
|
54
|
-
Every AI coding setup accumulates the same assets. Prompts to reuse, rules agents should follow, slash commands, skills, seed docs, sync scripts. Once you have enough projects, your copies drift and your agents stop getting consistent signals.
|
|
55
|
-
|
|
56
|
-
Three design choices shape the toolkit.
|
|
57
|
-
|
|
58
|
-
- Agent-first: every command has a non-interactive path and a JSON catalog. If a Claude Code skill or any other agent cannot drive the CLI without prompts, the design is wrong.
|
|
59
|
-
- Text-native: conventions, rules, and prompts are authored as markdown that you and your agents read the same way. No hidden behavior, no compiled state.
|
|
60
|
-
- One source, many consumers: this repo is the authoritative copy. Your projects install and sync on demand, never author in place.
|
|
61
|
-
|
|
62
|
-
Two limits worth knowing before you install. Claude Code is the only agent runtime the plugin targets, and the CLI needs Bun on your path. Snippets are the one surface that also travels to Gemini chat.
|
|
63
|
-
|
|
64
|
-
## What is inside
|
|
65
|
-
|
|
66
|
-
Each domain has a canonical source in this repo and a thin install or sync CLI on your side. The links run to internal narrative, written for someone maintaining the toolkit rather than installing it, so skip them on a first pass.
|
|
67
|
-
|
|
68
|
-
- [Claude Code plugin](.claude/context/claude-plugin/index.md): skills for planning, review, docs sync, and the git ship chain
|
|
69
|
-
- [Governance rules](.claude/context/governance/index.md): Claude rules and stacks, installable per project
|
|
70
|
-
- [Standards](.claude/context/standards/index.md): shared authoring conventions, read by name rather than installed
|
|
71
|
-
- [Snippets](.claude/context/snippets.md): reusable prompts for Claude and Gemini chat
|
|
72
|
-
- [Tooling stacks](.claude/context/tooling.md): golden configs, seeds, and references per framework
|
|
73
|
-
- [Design system](.claude/context/design.md): `DESIGN.md` token shape, extract skill and its two paths, render command
|
|
74
|
-
- [Slides](.claude/context/slides.md): `SLIDES.md` source, layout catalog, render command, draft skill
|
|
75
|
-
- [Transcripts](.claude/context/transcripts.md): fetch a YouTube transcript with metadata frontmatter via `canon transcripts`
|
|
76
|
-
- [Sandbox](.claude/context/sandbox/index.md): scenario-based scaffolds for verifying each domain flow
|
|
77
|
-
|
|
78
|
-
## Documentation
|
|
79
|
-
|
|
80
|
-
Scaffolding your first project? Start with target projects, then the AI workflow loop. Everything else answers questions that arrive later.
|
|
81
|
-
|
|
82
|
-
- [AI workflow](docs/ai-workflow.md): feature-development loop inside a toolkit-managed project
|
|
83
|
-
- [Operating model](docs/operating-model.md): orchestrator, planner, and worker roles for building across parallel sessions
|
|
84
|
-
- [Visual design workflow](docs/visual-design-workflow.md): tiered guide for design and wireframe authoring
|
|
85
|
-
- [Target projects](docs/target-projects.md): scaffold, add a domain later, sync upstream drift
|
|
86
|
-
- [Agents](docs/agents/index.md): CLI flags, exit codes, and JSON output shapes
|
|
87
|
-
- [Docs index](docs/index.md): every reference doc in this repo
|
|
88
|
-
|
|
89
89
|
## Development
|
|
90
90
|
|
|
91
91
|
Working on the toolkit starts from a clone. Running the CLI doesn't, since it installs from the registry. Skip this section unless you're changing the toolkit itself.
|
|
@@ -97,7 +97,7 @@ Working on the toolkit starts from a clone. Running the CLI doesn't, since it in
|
|
|
97
97
|
- [GitHub CLI](https://cli.github.com) (optional) for ship flows
|
|
98
98
|
- Shell: `zsh` or bash 4+ (`brew install bash` on macOS).
|
|
99
99
|
|
|
100
|
-
Clone the repo, then run the bootstrap script. It installs dependencies, links the CLI globally, and
|
|
100
|
+
Clone the repo, then run the bootstrap script. It installs dependencies, links the CLI globally, and appends a marked block of Claude Code shell aliases to your `~/.zshrc`.
|
|
101
101
|
|
|
102
102
|
```bash
|
|
103
103
|
git clone https://github.com/erclx/canon.git
|
|
@@ -106,7 +106,7 @@ bun install
|
|
|
106
106
|
bun run bootstrap
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
The script is idempotent, so re-run it after pulling upstream changes without duplicating anything. It confirms the install by resolving `canon --help` on the last step. See [zshrc aliases](docs/zshrc-aliases.md) for what each alias does.
|
|
109
|
+
The script is idempotent, so re-run it after pulling upstream changes without duplicating anything. That also means it leaves an alias block you already have alone rather than refreshing it, so an alias added upstream needs the block deleted and the script re-run. It confirms the install by resolving `canon --help` on the last step. See [zshrc aliases](docs/zshrc-aliases.md) for what each alias does, how to pick up a new one, and how to opt out of the block.
|
|
110
110
|
|
|
111
111
|
With the CLI linked, scaffold a fresh project.
|
|
112
112
|
|
|
@@ -120,6 +120,20 @@ canon init
|
|
|
120
120
|
|
|
121
121
|
For the full journey from scaffold through adding a domain later to syncing upstream drift, see [target projects](docs/target-projects.md).
|
|
122
122
|
|
|
123
|
+
### Internal narrative
|
|
124
|
+
|
|
125
|
+
Each domain carries an entry written for someone maintaining the toolkit rather than installing it. These paths resolve in a clone only. The published package ships `docs` and not `.claude`, so an installed copy does not carry them.
|
|
126
|
+
|
|
127
|
+
- [Claude Code plugin](.claude/context/claude-plugin/index.md)
|
|
128
|
+
- [Governance rules](.claude/context/governance/index.md)
|
|
129
|
+
- [Standards](.claude/context/standards/index.md)
|
|
130
|
+
- [Snippets](.claude/context/snippets.md)
|
|
131
|
+
- [Tooling stacks](.claude/context/tooling.md)
|
|
132
|
+
- [Design system](.claude/context/design.md)
|
|
133
|
+
- [Slides](.claude/context/slides.md)
|
|
134
|
+
- [Transcripts](.claude/context/transcripts.md)
|
|
135
|
+
- [Sandbox](.claude/context/sandbox/index.md)
|
|
136
|
+
|
|
123
137
|
## Contributing
|
|
124
138
|
|
|
125
139
|
Portfolio project. Issues are welcome. Pull requests are accepted by invitation only, so open an issue rather than a branch. Read the [contributing guidelines](CONTRIBUTING.md) for the local loop, the authoring split, and the commit convention before you file anything.
|
|
@@ -38,10 +38,10 @@ The trigger side carries a gap of its own. "Sync the docs" names either corpus t
|
|
|
38
38
|
## Must not
|
|
39
39
|
|
|
40
40
|
- Infer a new task from the diff. Only outcomes already on the board get marked.
|
|
41
|
-
- Touch task files the session did not change
|
|
41
|
+
- Touch task files the session did not change
|
|
42
42
|
- Widen what a writing step reads when the baseline is unusable. Widening a read is safe and widening a write stubs a surface for every file in the repository.
|
|
43
43
|
- Edit `CLAUDE.md` inline. Every change there goes through a diff-and-approve gate, so this skill only flags.
|
|
44
|
-
- Create a context entry or delete a plan
|
|
44
|
+
- Create a context entry, or move or delete a plan. A plan is settled by the merge, and `canon tasks archive` carries it.
|
|
45
45
|
- Overwrite a file a promotion block routes to. A destination that already holds a page is a merge for a person, and folding over it discards work this skill never read.
|
|
46
46
|
- Write an anchor onto a decision the run did not amend, or refresh one without re-reading the number. A date from a pass that measured nothing is the false confidence the marker exists to prevent.
|
|
47
47
|
- Anchor an entry written before the rule, which dates it by blame rather than by a read
|
|
@@ -51,7 +51,7 @@ Read these in parallel from the current worktree root (`pwd`), not the main work
|
|
|
51
51
|
|
|
52
52
|
Read the task board from the main worktree root instead, per Worktrees in `CLAUDE.md`. It is gitignored scratch and never commits with the branch:
|
|
53
53
|
|
|
54
|
-
- `.canon/tasks/index.md` first, then the task files this session touched. That narrow read serves the marking step
|
|
54
|
+
- `.canon/tasks/index.md` first, then the task files this session touched. That narrow read serves the marking step, which is the only step here that opens a task file.
|
|
55
55
|
|
|
56
56
|
## Step 2: identify what changed
|
|
57
57
|
|
|
@@ -102,7 +102,7 @@ For each doc with relevant changes, apply updates following these rules. Read a
|
|
|
102
102
|
|
|
103
103
|
The verb resolves the board at the main worktree root in-process, which is the route because this is an edit inside an existing file and the file-editing tools refuse that path from a linked worktree.
|
|
104
104
|
|
|
105
|
-
Read `ok` and `reason` out of that record rather than the exit
|
|
105
|
+
Read `ok` and `reason` out of that record rather than the exit. An operator's shell profile may wrap `canon` in a function that runs the binary and then a second command and takes the second status, which flattens every non-zero exit to zero. A refusal arriving as success leaves the outcome unmarked while the chain moves on, so the board reports shipped work as open and the next session re-plans it.
|
|
106
106
|
|
|
107
107
|
**REQUIREMENTS.md, ARCHITECTURE.md, DESIGN.md, `.claude/wireframes/<surface>.md`**
|
|
108
108
|
|
|
@@ -208,40 +208,13 @@ Report a block left unfolded rather than dropping it:
|
|
|
208
208
|
|
|
209
209
|
`⚠ Skipped: <destination path> already exists. Merge by hand.`
|
|
210
210
|
|
|
211
|
-
## Step 9: sweep consumed
|
|
211
|
+
## Step 9: sweep consumed receipts
|
|
212
212
|
|
|
213
|
-
Sweep
|
|
213
|
+
Sweep the review and memory receipts this session consumed. Resolve all paths at the main worktree root, not the current worktree. See Worktrees in `CLAUDE.md`.
|
|
214
214
|
|
|
215
|
-
Every
|
|
215
|
+
Every delete below is a shell operation, so send each as a plain single `Bash` command rather than joining two with `&&`, which is refused as compound from a linked worktree.
|
|
216
216
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
Scan every file in `.canon/tasks/`, not only the ones this session touched. For each task file whose outcomes are now all `[x]`, check for a `Plan:` line directly under the title and parse the target.
|
|
220
|
-
|
|
221
|
-
The line carries a markdown link, so read the target out of the parentheses rather than taking the rest of the line. A task still carrying the older bare-path form parses the same way once the link is absent, so accept both. Resolve the target against `.canon/tasks/` before routing on it, which lands `../plans/x.md` and `.canon/plans/x.md` on the same file.
|
|
222
|
-
|
|
223
|
-
The bullets below name resolved locations, so an unresolved target falls to the last one and no plan is ever archived. Never delete a plan. `${CLAUDE_SKILL_DIR}/../../standards/plan.md` owns the archive destination and why a shipped plan is moved rather than removed.
|
|
224
|
-
|
|
225
|
-
Board-wide scope is the one place this sweep reaches past Step 3's rule against touching task files the session did not change. A board carrying a task that closed while an earlier run missed its archive is the defect this exists to clear, and skipping those tasks would preserve it. Reaching them is safe because the archive moves the plan and points the task at the new path, so a task from unrelated work ends up with a working pointer rather than a broken one.
|
|
226
|
-
|
|
227
|
-
Before moving anything, count the other citations. Scan every `.canon/tasks/*.md` file except the one being processed for a `Plan:` line naming the same plan. Compare the resolved target from the parse above, never the raw target string and never the filename alone.
|
|
228
|
-
|
|
229
|
-
A board carrying one task written `../plans/x.md` and another written `.canon/plans/x.md` cites one plan, and a raw string comparison reads two, counts zero, and archives the file out from under a live task. Comparing filenames swaps that for the opposite error, since a live plan and an archived one share a basename whenever a closed task still points into `.canon/plans/archive/`, and the count then reads a citation that does not exist and archives nothing.
|
|
230
|
-
|
|
231
|
-
Exclude the closing task explicitly. It sits on the board and cites the plan itself, so a scan that counts it never reaches zero and no plan is ever archived.
|
|
232
|
-
|
|
233
|
-
`canon tasks plan-citations <stem> --json` answers this same question, and the archive gate already reads it. This body states the rule anyway rather than calling the verb, because a plugin skill reaches a target the moment it merges while the CLI reaches one only when a release publishes, so a target whose installed `canon` predates the verb gets no record back and routes on nothing. Measured against the `claude:docs` `board-sweep` arm, where calling the verb archived neither plan and created no `.canon/plans/archive/`.
|
|
234
|
-
|
|
235
|
-
Nothing in the exit code reports that. Branch on the record's `ok` and `reason` fields and never on the exit, which is the rule every task verb already carries: an operator's shell profile may wrap `canon` in a function that runs the binary and then another command, taking its status from the second, and one measured here masks every non-zero exit rather than only an absent verb. The binary itself exits 1 for an unknown subcommand and 1 for an ordinary refusal alike. Switching this body to the verb needs a release that carries it and a read of the record rather than the exit, which together retire the duplication.
|
|
236
|
-
|
|
237
|
-
A plan can serve more than one task, and archiving on the first task to close strands every other task's pointer at a path that has moved. `.canon/plans/` is gitignored, so that retarget would be the only record and there is nothing to recover it from.
|
|
238
|
-
|
|
239
|
-
- Target resolves inside `.canon/plans/`, the file exists, and no other task file cites it: create `.canon/plans/archive/`, move the file there under its original name, overwriting any file already sitting at that name. Then rewrite the task file's `Plan:` line to the archive path, so a completed task still leads to the reasoning behind it.
|
|
240
|
-
- Target resolves inside `.canon/plans/` and at least one other task file cites it: leave the plan where it is and retarget nothing. Report the shared citation.
|
|
241
|
-
- Target resolves inside `.canon/plans/archive/`: skip silently. The plan was archived by an earlier pass and the task file is already correct.
|
|
242
|
-
- Any other resolved target outside `.canon/plans/`: warn and skip.
|
|
243
|
-
|
|
244
|
-
Write the retarget as a markdown link, `Plan: [feature-<slug>](../plans/archive/feature-<slug>.md)`, updating both halves so the text and the target stay in step. This branch is the only writer that produces a `Plan:` line nobody authored by hand, so a retarget that emits a bare path converts every task to the old form as it closes and drifts the board back to two shapes on its own.
|
|
217
|
+
Plans are not swept here. A plan is settled by the merge rather than by an outcome this run marked, and `canon tasks archive` moves it with the task the `post-merge` hook archives. Sweeping it from this step read a closure Step 3 had written moments earlier and moved a plan the branch was still building from.
|
|
245
218
|
|
|
246
219
|
### Reviews
|
|
247
220
|
|
|
@@ -253,7 +226,7 @@ Sweep the branch reports this session never opened. List `.canon/review/branch/r
|
|
|
253
226
|
|
|
254
227
|
What that removes is a local-only review on a branch deleted before it opened a pull request. `claude-review` says so where a reader meets the report, and the sweep runs anyway rather than keeping every report against the one case, since nothing else ever clears them.
|
|
255
228
|
|
|
256
|
-
Memory receipts sweep board-wide
|
|
229
|
+
Memory receipts sweep board-wide rather than by slug. Scan every `.canon/review/memory/memory-review-*.md`, not only the one matching this slug. `claude-memory-review` writes its receipt after this skill has run in every ship chain, so a sweep keyed on the current slug looks for a file that does not exist yet, and no later branch looks for it either because a slug is unique per feature. Scanning the folder is what makes the sweep fire at all.
|
|
257
230
|
|
|
258
231
|
For each receipt, count the H2 items still marked 📝 pending:
|
|
259
232
|
|
|
@@ -266,8 +239,6 @@ Do not sweep `ui-checklist-*.md` (pending human verification), `ux-audit-*.md`,
|
|
|
266
239
|
|
|
267
240
|
Output one line per file swept:
|
|
268
241
|
|
|
269
|
-
- `📦 Archived: <path>` for a plan moved into `.canon/plans/archive/`
|
|
270
|
-
- `⏭ Kept: <path>, still cited by <task-file>` for a plan another live task shares
|
|
271
242
|
- `🧹 Deleted: <path>, branch gone` for a branch report whose branch no longer exists
|
|
272
243
|
- `🧹 Deleted: <path>, folded <n> skips` for a swept memory receipt
|
|
273
244
|
- `⏭ Kept: <path>, <n> items pending` for a memory receipt still holding decisions
|
|
@@ -73,13 +73,17 @@ Name `<model>` on the launch, and pick it against the task rather than copying w
|
|
|
73
73
|
## Dispatch
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
|
-
claude --bg --model <model> -n "worker-<slug>" "Run /canon:claude-worktree <type>/<slug>, then /canon:claude-autoship <plan>. Your controller is the session whose sessionId is <dispatcher-id>. Resolve its current name from that id at the moment you send, and never resolve an addressee by name prefix. Message it when the pull request opens, carrying the number, the branch, the head sha, the CI state, and every point you departed from the plan on, and message it again if you stop on a question."
|
|
76
|
+
claude --bg --model <model> -n "worker-<project>-<slug>" "Run /canon:claude-worktree <type>/<slug>, then /canon:claude-autoship <plan>. Your controller is the session whose sessionId is <dispatcher-id>. Resolve its current name from that id at the moment you send, and never resolve an addressee by name prefix. Message it when the pull request opens, carrying the number, the branch, the head sha, the CI state, and every point you departed from the plan on, and message it again if you stop on a question."
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
`--bg, --background` starts the session as a background agent and returns immediately, `-n, --name` sets the display name that tells a self-dispatched worker from an operator's own launch in `canon sessions list`, and `--model` overrides the inheritance the section above measured. Pass `-n` on every dispatch rather than letting the client derive one. A launch that omits it leaves the session named for a fragment of its own identifier, which is both its address on the send channel and the whole of what the operator sees for it in agent view.
|
|
80
80
|
|
|
81
81
|
The prefix reads `worker-` because that is the role it marks. It read `orchestrator-` until 2026-08-31, and no controlling session ever carried it, so a worker filtering the roster for that string found a sibling or itself on every row. Nothing matches the prefix programmatically, which is what kept the rename down to three strings.
|
|
82
82
|
|
|
83
|
+
`<project>` is the basename of the main worktree root, not of wherever the dispatcher happens to be running. Resolve the main root first, the way `claude-worktree` Step 1 does, since a bare `git rev-parse --show-toplevel` inside a linked worktree returns the worktree path rather than the project's. See Worktrees in `CLAUDE.md`.
|
|
84
|
+
|
|
85
|
+
`claude agents` lists every session on the machine with no path column and no per-project filter, so `<project>` in the name is the only thing left telling two fleets apart, and a session named off the worktree path instead would carry the branch folder rather than the project. Two projects each dispatching a bare `worker-page-driver` used to read as one row in that view.
|
|
86
|
+
|
|
83
87
|
Read `<dispatcher-id>` with `canon sessions list --self --json` and interpolate the `sessionId` that row carries. Carry the id rather than the name. A name is derived from whatever the session turned out to be doing, and across the 181 records stamping both fields, nine were renamed after launch at a median of 5.4 minutes and a maximum of 509. Three landed more than ten minutes in, which is inside the window a worker announces its pull request in, so a name written into the prompt is aimed at a send that happens after it goes stale.
|
|
84
88
|
|
|
85
89
|
Where the installed CLI answers `--self` with an unknown option, that flag is newer than the release the target holds. Read the `sessionId` from the record the client writes for this session under its configuration directory, and say which route answered so the reader knows whether the id was read or inferred.
|
|
@@ -165,11 +169,11 @@ directly with `Bash`, `Read`, and `Edit` instead of retrying the tool, which
|
|
|
165
169
|
is the route two workers already took today on two different branches.
|
|
166
170
|
|
|
167
171
|
```bash
|
|
168
|
-
claude --bg --model <model> -n "worker-<slug>" "Enter the worktree for <branch> at .claude/worktrees/<slug>/, creating it from that branch if the folder is gone. Run /canon:claude-worker, then /canon:claude-address-review. Your controller is the session whose sessionId is <dispatcher-id>. Resolve its current name from that id at the moment you send, and never resolve an addressee by name prefix. Message it when the address pass finishes, carrying what was addressed and the PR's CI state, and message it again if you stop on a question."
|
|
172
|
+
claude --bg --model <model> -n "worker-<project>-<slug>" "Enter the worktree for <branch> at .claude/worktrees/<slug>/, creating it from that branch if the folder is gone. Run /canon:claude-worker, then /canon:claude-address-review. Your controller is the session whose sessionId is <dispatcher-id>. Resolve its current name from that id at the moment you send, and never resolve an addressee by name prefix. Message it when the address pass finishes, carrying what was addressed and the PR's CI state, and message it again if you stop on a question."
|
|
169
173
|
```
|
|
170
174
|
|
|
171
|
-
`<dispatcher-id
|
|
172
|
-
them above.
|
|
175
|
+
`<dispatcher-id>`, `<model>`, and `<project>` resolve the same way the build
|
|
176
|
+
shape resolves them above.
|
|
173
177
|
|
|
174
178
|
Take this shape wherever a review needs answering and no live session already
|
|
175
179
|
holds the branch. Where one does, message it to run `claude-address-review`
|
|
@@ -196,14 +200,15 @@ gitignored file at the main worktree root, so this shape names the row's task
|
|
|
196
200
|
file rather than a branch and opens with the role instead of a worktree call.
|
|
197
201
|
|
|
198
202
|
```bash
|
|
199
|
-
claude --bg --model <model> -n "planner-<slug>" "Run /canon:claude-planner, then /canon:claude-feature <task>. Your controller is the session whose sessionId is <dispatcher-id>. Resolve its current name from that id at the moment you send, and never resolve an addressee by name prefix. Message it when the plan lands, carrying the path and what the task file got wrong, and message it again if you stop on a question."
|
|
203
|
+
claude --bg --model <model> -n "planner-<project>-<slug>" "Run /canon:claude-planner, then /canon:claude-feature <task>. Your controller is the session whose sessionId is <dispatcher-id>. Resolve its current name from that id at the moment you send, and never resolve an addressee by name prefix. Message it when the plan lands, carrying the path and what the task file got wrong, and message it again if you stop on a question."
|
|
200
204
|
```
|
|
201
205
|
|
|
202
206
|
`<task>` is the row's task file path and `<slug>` the slug its plan will take,
|
|
203
207
|
resolved off the row the way the build shape resolves one off a plan.
|
|
204
|
-
`<dispatcher-id
|
|
205
|
-
reads `planner-` for the reason the worker's reads `worker-`,
|
|
206
|
-
marks the role of the session it names rather than the one
|
|
208
|
+
`<dispatcher-id>`, `<model>`, and `<project>` resolve the same way they do
|
|
209
|
+
above. The prefix reads `planner-` for the reason the worker's reads `worker-`,
|
|
210
|
+
which is that it marks the role of the session it names rather than the one
|
|
211
|
+
that launched it.
|
|
207
212
|
|
|
208
213
|
None of the three checks above binds this shape. The branch check has no
|
|
209
214
|
candidate to read, and the disjointness gate has nothing to compare, since a
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: claude-tasks
|
|
3
|
-
description: Creates a task file in `.canon/tasks/` with the filename, phase label, and frontmatter the standard requires, and archives a shipped one out of the folder. Use when asked to "add a task", "create a task", "queue this", "put this on the board", "archive that task", or "close out a shipped task". Do NOT use to mark an outcome `[x]
|
|
3
|
+
description: Creates a task file in `.canon/tasks/` with the filename, phase label, and frontmatter the standard requires, and archives a shipped one out of the folder. Use when asked to "add a task", "create a task", "queue this", "put this on the board", "archive that task", or "close out a shipped task". Do NOT use to mark an outcome `[x]`. That is `claude-docs`.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Claude tasks
|
|
7
7
|
|
|
8
|
-
Owns the two operations that bring a task file into existence and take it out of the folder. `claude-docs` edits the contents of a task that already exists, marking outcomes `[x]
|
|
8
|
+
Owns the two operations that bring a task file into existence and take it out of the folder. `claude-docs` edits the contents of a task that already exists, marking outcomes `[x]`. Do not mark outcomes here, and do not move a plan by hand: the archive carries it.
|
|
9
9
|
|
|
10
10
|
Read `${CLAUDE_SKILL_DIR}/../../standards/tasks.md` before writing any file. It holds the filename convention, the frontmatter contract, and the file format. Do not work them from memory.
|
|
11
11
|
|
|
@@ -104,19 +104,18 @@ canon tasks archive <stem> --json
|
|
|
104
104
|
|
|
105
105
|
The command refuses rather than reports, and the refusal reaches this skill through the record rather than through the exit. Branch on `ok`, then on `reason`. An operator's shell profile may wrap `canon` in a function that runs the binary and then a second command and takes the second status, which masks every non-zero exit rather than only an absent verb. The binary exits 1 for an unknown subcommand and 1 for an ordinary refusal alike, so the record is the only signal that survives the wrapper.
|
|
106
106
|
|
|
107
|
-
On success the record carries `from`, `to`, `priorityRowRemoved`, and `indexRegenerated`, which is what moved, what row it cleared, and whether the index changed.
|
|
107
|
+
On success the record carries `from`, `to`, `priorityRowRemoved`, and `indexRegenerated`, which is what moved, what row it cleared, and whether the index changed. It also carries `plan` when the task was the last live citation of a live plan, holding the `from` and `to` of the plan moved alongside it. A `plan` of `null` means the task cited no live plan, or that a sibling still holds it.
|
|
108
108
|
|
|
109
109
|
### Step 3: route on a refusal
|
|
110
110
|
|
|
111
111
|
Each reason has one resolution and none of them is to archive around it:
|
|
112
112
|
|
|
113
113
|
- `open-outcomes`: the named outcomes are unmarked or genuinely open. Run `claude-docs` when the work shipped and nothing marked it. Leave the task on the board when the outcome is real. Cut the outcomes first when the work is being abandoned, so the board records what was dropped.
|
|
114
|
-
- `plan-unswept`: stop and route to `claude-docs`, which owns the plans sweep and the last-live-citation rule. `❌ Plan not yet swept. Run /claude-docs first, then archive.`
|
|
115
114
|
- `ambiguous`: two tasks name one pull request, which is the misfile `${CLAUDE_SKILL_DIR}/../../standards/tasks.md` rules out. Resolve the citation by hand, since no sweep repairs it.
|
|
116
115
|
- `no-match`: the stem or number names nothing on the board. Check the name against the listed stems.
|
|
117
116
|
- `bad-input`: the command line was wrong rather than the board. Read the message, fix the arguments, and run it again. Nothing on the board needs repair, which is what separates this from the two above.
|
|
118
117
|
|
|
119
|
-
Do not move a plan from this skill.
|
|
118
|
+
Do not move a plan by hand from this skill. The command carries the plan with the task when no other live task cites it, and retargets the archived task's `Plan:` line at the new path. A second mover drifts into relocating the same file differently.
|
|
120
119
|
|
|
121
120
|
Leave `TASK-ARCHIVE.md` alone when it is present in the archive folder. It records the single-file era in the shape that era used, and splitting it would fabricate per-task files nobody wrote.
|
|
122
121
|
|
|
@@ -95,12 +95,22 @@ canon teach lesson <topic> --json \
|
|
|
95
95
|
It writes nothing and reports four things:
|
|
96
96
|
|
|
97
97
|
- `lesson`, the numbered path the lesson takes. Write it there.
|
|
98
|
-
- `stylesheet` with `stylesheetExists` and `stylesheetHref`.
|
|
98
|
+
- `stylesheet` with `stylesheetExists` and `stylesheetHref`. Link it at `stylesheetHref` and seed it through the verb below when `stylesheetExists` is false, which is the first lesson in the workspace. Every lesson after that links the file already on disk and adds to it rather than replacing it, since overwriting discards what the last lesson put there.
|
|
99
99
|
- `success`, the mission's success lines, carried here so Step 5 needs no second read.
|
|
100
100
|
- `quiz`, one entry per question, carrying `order` and `answer`.
|
|
101
101
|
|
|
102
102
|
Write the correct option first, then present the options in the order `order` reports, reading it as authored indices where `0` is the correct one. Take the order as given. Position drawn here rather than chosen is the whole reason the verb exists, and a lesson that reorders on its own judgment puts the answer back in the first slot.
|
|
103
103
|
|
|
104
|
+
Seed the stylesheet through the verb rather than authoring a palette, on the first lesson in a workspace:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
canon teach stylesheet <topic> --json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
It writes the design tokens as custom properties and the components built on them, from the one source every other rendered surface reads. Add lesson rules under the seed and reach a value through its property rather than restating the hex, which is what let each workspace fork the palette from every other. It refuses to overwrite, so running it again on a workspace that has grown its own rules is safe and reports `written` as false.
|
|
111
|
+
|
|
112
|
+
Report it rather than proceeding silently when the verb does not resolve, which is an installed CLI predating it. Do not fall back to writing a palette by hand.
|
|
113
|
+
|
|
104
114
|
Add every term the lesson defines to `GLOSSARY.md` through the verb, which places the entries alphabetically in the shape the standard fixes:
|
|
105
115
|
|
|
106
116
|
```bash
|
|
@@ -72,8 +72,8 @@ Infer only where no operator is present. Read `canon sessions list --json` and
|
|
|
72
72
|
take the sessions holding no feature branch as the candidates, since a
|
|
73
73
|
controlling session holds none. Say the addressee was inferred so the reader can
|
|
74
74
|
correct it. Never filter that roster by name prefix: every self-dispatched
|
|
75
|
-
worker is named `worker-<slug>`, so a prefix scan returns a sibling or
|
|
76
|
-
session itself, which is the defect that sent messages owed to a controller
|
|
75
|
+
worker is named `worker-<project>-<slug>`, so a prefix scan returns a sibling or
|
|
76
|
+
this session itself, which is the defect that sent messages owed to a controller
|
|
77
77
|
somewhere else.
|
|
78
78
|
|
|
79
79
|
Report a resolution that returns nothing rather than falling back to a guess.
|
package/docs/agents/commands.md
CHANGED
|
@@ -22,11 +22,15 @@ Full help: `canon <command> --help`. Behavior notes for the install and sync ver
|
|
|
22
22
|
| `canon indexes regen` | Regenerate `index.md` files from sibling frontmatter |
|
|
23
23
|
| `canon docs [topic]` | Emit toolkit reference docs (`list`, or a topic by name) |
|
|
24
24
|
| `canon design render` | Render `.claude/DESIGN.md` tokens to HTML and CSS |
|
|
25
|
+
| `canon design regen` | Rewrite this repository's `.claude/DESIGN.md` and `src/design/base.css` from `src/design/tokens.ts` |
|
|
26
|
+
| `canon design css` | Emit the design tokens and components as CSS on stdout (`--no-components` for properties alone) |
|
|
27
|
+
| `canon design install` | Install the base stylesheet into a project at `.claude/design/base.css` |
|
|
28
|
+
| `canon design sync` | Update a base stylesheet already installed under `.claude/design/` |
|
|
25
29
|
| `canon slides render` | Render a `.claude/SLIDES.md` source into a PowerPoint deck, reporting any unrecognized layout name on stderr |
|
|
26
30
|
| `canon slides list` | List the available slide layouts (`--json` for the catalog) |
|
|
27
31
|
| `canon feedback` | Write toolkit feedback from stdin to `.canon/review/feedback/`, or open a GitHub issue with `--github` |
|
|
28
32
|
| `canon transcripts <url>` | Fetch a YouTube transcript with metadata frontmatter (needs `yt-dlp`) |
|
|
29
|
-
| `canon tasks archive` | Move a shipped task off the board, clear its ordering row, and regenerate the index
|
|
33
|
+
| `canon tasks archive` | Move a shipped task and its plan off the board, clear its ordering row, and regenerate the index |
|
|
30
34
|
| `canon tasks pull-request` | Record a pull request number on the task a branch closes, by stem or `--plan` (`--json`) |
|
|
31
35
|
| `canon tasks outcome` | Mark outcomes `[x]` on a task by position, repeating `--close` (`--json`) |
|
|
32
36
|
| `canon tasks validate` | Report board rows whose shape, order, plan, task file, group, file set, or blocker does not hold (`--json`) |
|
|
@@ -36,6 +40,7 @@ Full help: `canon <command> --help`. Behavior notes for the install and sync ver
|
|
|
36
40
|
| `canon teach open` | Open a workspace at the next ordinal with its mission, resources, and glossary files (`--json`) |
|
|
37
41
|
| `canon teach resource` | Record sources and leads in a workspace, repeating `--read` or `--lead` as `<title>=<url>` (`--json`) |
|
|
38
42
|
| `canon teach glossary` | Add terms to a workspace glossary alphabetically, repeating `--term <term>=<definition>` (`--json`) |
|
|
43
|
+
| `canon teach stylesheet` | Seed a workspace stylesheet from the design source, leaving an existing one alone without `--force` (`--json`) |
|
|
39
44
|
| `canon records validate` | Report a session record or a standard against the standard governing it, per kind (`--json`) |
|
|
40
45
|
| `canon records migrate` | Rewrite the records a validate finding names a recoverable transform for (`--write`, `--json`) |
|
|
41
46
|
| `canon records size` | Report what each record folder holds and how much of it is recent, heaviest first (`--json`) |
|
|
@@ -112,11 +117,11 @@ Each domain exposes a consistent shape where applicable: `list`, `install`, `syn
|
|
|
112
117
|
| `demo` | `compile`, `run` |
|
|
113
118
|
| `inventory` | `run` |
|
|
114
119
|
| `wiki` | `init` |
|
|
115
|
-
| `design` | `render`
|
|
120
|
+
| `design` | `regen`, `css`, `render`, `install`, `sync` |
|
|
116
121
|
| `slides` | `render`, `list` |
|
|
117
122
|
| `tasks` | `archive`, `validate` |
|
|
118
123
|
| `intake` | `list`, `answer` |
|
|
119
|
-
| `teach` | `list`, `open`, `resource`, `glossary`
|
|
124
|
+
| `teach` | `list`, `open`, `resource`, `glossary`, `lesson`, `stylesheet` |
|
|
120
125
|
| `comments` | `scan` |
|
|
121
126
|
| `context` | `audit` |
|
|
122
127
|
| `markdown` | `audit` |
|
|
@@ -27,7 +27,29 @@ number bands behind that placement, where `900-999` is the range a
|
|
|
27
27
|
project-authored rule takes and everything below it belongs to the toolkit.
|
|
28
28
|
|
|
29
29
|
`canon sync --check` does not report an orphaned entry. It skips every one, so
|
|
30
|
-
the destination reaches `canon gov sync` alone
|
|
30
|
+
the destination reaches `canon gov sync` alone among the two per-file domain
|
|
31
|
+
syncs, since design's own orphans are the target's overrides and are meant to
|
|
32
|
+
stay where they are.
|
|
33
|
+
|
|
34
|
+
`canon design install` copies one toolkit-owned file to
|
|
35
|
+
`.claude/design/base.css` and creates no override. A project overrides a value
|
|
36
|
+
by writing `.claude/design/project/` itself, which `canon design sync` never
|
|
37
|
+
touches, because that subfolder is project-authored by location the way
|
|
38
|
+
`.claude/rules/project/` is. An override named exactly like the shipped file is
|
|
39
|
+
still the project's.
|
|
40
|
+
|
|
41
|
+
The override ships absent rather than empty. An empty file is one the project
|
|
42
|
+
did not ask for and did not write, the reconciliation already handles a missing
|
|
43
|
+
side, and an empty override invites a target to fill it before it has an
|
|
44
|
+
opinion.
|
|
45
|
+
|
|
46
|
+
Nothing arrives on a project that has not run `canon design install`. The
|
|
47
|
+
domain is detected by that folder existing, so `canon sync` skips it entirely on
|
|
48
|
+
a target that never installed it, and the unstamped line at the end of
|
|
49
|
+
`canon sync --check` stays quiet about it for the same reason. Governance is
|
|
50
|
+
named there when it is absent and design is not, because a managed project
|
|
51
|
+
without governance has yet to install what every project carries, while one
|
|
52
|
+
without design chose that.
|
|
31
53
|
|
|
32
54
|
When the target's install recorded a stack, `canon gov sync` also reports a
|
|
33
55
|
rule that stack lists and `.claude/rules/` does not hold, as a `missing` entry
|
package/docs/agents/overview.md
CHANGED
|
@@ -5,9 +5,9 @@ description: What this folder covers, the invocation rules every command inherit
|
|
|
5
5
|
|
|
6
6
|
# Overview
|
|
7
7
|
|
|
8
|
-
CLI catalog and invocation rules for
|
|
8
|
+
CLI catalog and invocation rules for an agent driving the `canon` CLI, in this repository or in any project that installed it.
|
|
9
9
|
|
|
10
|
-
This folder is an index of what an agent can run and how to run it cleanly from a script. It does not cover domain behavior. Read `CLAUDE.md` for
|
|
10
|
+
This folder is an index of what an agent can run and how to run it cleanly from a script. It does not cover domain behavior. Read the project's own `CLAUDE.md` for that. The `.claude/skills/internal-*` skills carry the per-domain editing guidance and live in the toolkit checkout alone, so a reader who installed the package resolves none of them.
|
|
11
11
|
|
|
12
12
|
## Invocation rules
|
|
13
13
|
|
|
@@ -22,5 +22,5 @@ See `CLAUDE.md` design principles. They apply to every command in this folder.
|
|
|
22
22
|
## Related
|
|
23
23
|
|
|
24
24
|
- `CLAUDE.md`: project behaviors and design principles
|
|
25
|
-
- `.claude/skills/internal-*`: domain-scoped guidance for editing work
|
|
25
|
+
- `.claude/skills/internal-*`: domain-scoped guidance for editing work, in the toolkit checkout only
|
|
26
26
|
- `docs/index.md`: full docs directory
|
package/docs/agents/tasks.md
CHANGED
|
@@ -22,9 +22,13 @@ canon tasks archive --pull-request 673 --json
|
|
|
22
22
|
| `--json` | Emit a machine-readable record on stdout |
|
|
23
23
|
| `--root <path>` | Board root, defaulting to the main worktree |
|
|
24
24
|
|
|
25
|
-
Exit codes: `0` archived, `1` refused. Every gate is a refusal rather than a warning, because `.husky/post-merge` calls this with nobody watching. The `reason` field carries which gate fired: `no-board`, `no-match`, `ambiguous`, `no-outcomes`, `open-outcomes`,
|
|
25
|
+
Exit codes: `0` archived, `1` refused. Every gate is a refusal rather than a warning, because `.husky/post-merge` calls this with nobody watching. The `reason` field carries which gate fired: `no-board`, `no-match`, `ambiguous`, `no-outcomes`, `open-outcomes`, or `bad-input`.
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
The task carries its plan with it. When the closing task is the last live one whose `Plan:` line resolves onto that file, the plan moves to `.canon/plans/archive/` under its own name and the archived task's line is rewritten as `Plan: [feature-<slug>](../../plans/archive/feature-<slug>.md)`, a folder deeper than the live task wrote it. The `plan` field on the success record carries that `from` and `to`, and is `null` when nothing moved.
|
|
28
|
+
|
|
29
|
+
A plan several tasks share stays where it is, and the task archives anyway. Moving it on the first task to close strands every sibling's pointer at a path that has gone, and `.canon/plans/` is gitignored so no history recovers the target. A `Plan:` line resolving to no file leaves the plan alone too, since a pointer somebody typed wrong is not a plan to move and holding the whole archive over it would park the board behind a repair the merge cannot make.
|
|
30
|
+
|
|
31
|
+
The merge is what settles a plan. `.husky/post-merge` reads the pull request number off the squash subject and calls this verb, so both halves close in one act with nobody naming a file. That is why the move sits inside this verb rather than in a second call the hook would make after it, which could leave the task archived and the plan live.
|
|
28
32
|
|
|
29
33
|
`bad-input` covers a malformed command line, which all three task verbs answer the same way. It is separate from `ambiguous` and `no-match` because those describe the board, and a caller that passed two selectors would otherwise be sent to repair a task citation that is fine.
|
|
30
34
|
|
|
@@ -53,7 +57,7 @@ The record carries `location`, one of `unstated`, `live`, `archived`, or `outsid
|
|
|
53
57
|
|
|
54
58
|
The target resolves against the board folder and against the project root both, so `../plans/x.md` and `.canon/plans/x.md` land on the same file and one plan two tasks spelled differently counts once. Containment is tested at both record roots rather than at the one this tree resolves at, since a line somebody wrote against a root the tree has since left is still a path into the plans folder, and reading it as outside would report a shipped plan as still live. `docs/agents/records.md` states the read order.
|
|
55
59
|
|
|
56
|
-
`canon tasks archive`
|
|
60
|
+
`canon tasks archive` decides its plan move on this same answer, so a caller wanting the count reads it here rather than scanning the board.
|
|
57
61
|
|
|
58
62
|
Branch on `reason` rather than on the exit code, which is the rule the archive section above already states and which this verb needs for a second reason. An operator's shell profile may wrap `canon` in a function that runs the binary and then another command and takes the second status, which masks every non-zero exit rather than only an absent verb. The binary exits 1 for an unknown subcommand and 1 for an ordinary refusal alike, so the record is the only signal that survives the wrapper.
|
|
59
63
|
|
|
@@ -159,7 +163,7 @@ Seven checks run. Plan and Collisions reach one half each of the `## Run now` te
|
|
|
159
163
|
| Check | What it reports |
|
|
160
164
|
| ---------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
161
165
|
| Shape | A row whose cell count disagrees with its table's header, or one stranded behind a table a blank line already closed |
|
|
162
|
-
| Plan | A `## Run now` row whose Plan column carries no link, or
|
|
166
|
+
| Plan | A `## Run now` row whose Plan column carries no link, resolves to no file, or disagrees with the task's own line |
|
|
163
167
|
| Mapping | A row or backlog line naming no task file, and a task file neither surface names |
|
|
164
168
|
| Grouping | A task carrying a row in more than one readiness group, or on both surfaces |
|
|
165
169
|
| Ordering | A `## Needs a plan` row whose stated position disagrees with where it actually sits |
|
|
@@ -168,6 +172,8 @@ Seven checks run. Plan and Collisions reach one half each of the `## Run now` te
|
|
|
168
172
|
|
|
169
173
|
Shape runs before any other check reads a row, since a row failing it carries no dependable fields for the rest to check. A blank or prose line closes the table above it, so the walk treats the next pipe line as a fresh header candidate rather than as a continuation. That candidate counts as a header only when the line behind it is a separator carrying the same cell count, and one that fails is `row-untabled`, stranded behind a table that already closed. Cell count still has to match the header on every row that clears that test, and a row whose count disagrees is `row-misshapen`, the shape a dropped pipe or a merged column produces.
|
|
170
174
|
|
|
175
|
+
The Plan check reads the row and the task file both, because the two are written by different hands and only the task's own `Plan:` line reaches the archive. A row carrying a plan whose task states none is `plan-uncited`, and a pair naming two different plans is `plan-mismatched`. Both sides resolve against the board and against the project root before they compare, so a row writing `../plans/x.md` and a task writing `.canon/plans/x.md` name one file rather than two.
|
|
176
|
+
|
|
171
177
|
Mapping spans two surfaces, because a task sits on `priority.md` when it would plausibly be planned soon and on `backlog.md` otherwise. A task file either surface names is accounted for, a file neither names is `row-missing`, and a file both name is `row-duplicated` for the reason a task in two groups is: it claims two things about itself and only one can hold. One check across both is what lets a task move between them without the move reading as a dropped file.
|
|
172
178
|
|
|
173
179
|
A backlog line is a bullet carrying a link to a sibling task, since the backlog is a flat unordered list rather than a table. A bullet holding prose is skipped rather than reported, which keeps the file's own intro out of the findings, and the task that bullet meant to name is still reported as reaching neither surface. A project carrying no `backlog.md` reads as an empty backlog rather than a refusal, which leaves the one-to-one mapping this check ran before the second surface existed.
|
package/docs/agents/teach.md
CHANGED
|
@@ -125,6 +125,8 @@ It reports four things. `lesson` is the numbered path the lesson takes, derived
|
|
|
125
125
|
|
|
126
126
|
The stylesheet is reported rather than written. A verb that wrote it on every lesson would discard whatever the last lesson added, and the second lesson in a workspace is the one that needs the file the first one left.
|
|
127
127
|
|
|
128
|
+
`canon teach stylesheet <topic>` is what writes it, seeded from the design source so a workspace renders in the system every other surface does. It refuses to overwrite, reporting `written` as false where the workspace already carries one, and `--force` takes the seed back over it. Each workspace used to carry a hand-authored palette, which is how the course styling forked once per workspace, so a lesson adds its own rules under the seed and reaches a value through its custom property rather than restating the hex.
|
|
129
|
+
|
|
128
130
|
Each `quiz` entry carries `order`, the authored option indices in presentation order where index `0` is the correct answer, and `answer`, the one-based position that answer lands in. Both travel together because a caller deriving the position itself is a caller that can derive it wrongly.
|
|
129
131
|
|
|
130
132
|
The order is drawn here rather than instructed, and that is the point of the verb. An author told to vary the position still varies it by judgment, and the judgment settles on the first slot, which is the defect this design departs from. The draw is uniform over the options, so the position carries no information about which answer is correct.
|
package/docs/ai-workflow.md
CHANGED
|
@@ -108,12 +108,10 @@ A person points it at a private repository once and both verbs refuse until they
|
|
|
108
108
|
|
|
109
109
|
`canon records size` reports what each of these folders holds, heaviest first, along with `.canon/tmp`. Each row carries the file count, the bytes, how many files were written in the last 7 and 30 days, and the dates of the least and most recently written one. Nothing fails on a number, because a record folder has no correct size. What the verb replaces is a reading somebody had to remember to take: the memory pen went from 44 entries to 236 between two counts made by hand a fortnight apart, and nothing reported the rate in between.
|
|
110
110
|
|
|
111
|
-
A plan that ships is archived, never deleted. `canon
|
|
111
|
+
A plan that ships is archived, never deleted. `canon tasks archive` moves it to `.canon/plans/archive/` alongside the task it belonged to and retargets that task's `Plan:` line at the new location, so a completed task still leads to the reasoning behind it. An archive sits inside the record folder it archives rather than beside it, so one ignore entry and one backed-folder entry cover a record and everything it has retired. The folder is gitignored, which is why a deleted plan had no recovery path. A plan cited by more than one task stays put until the last of them closes, since moving it early would strand every other pointer.
|
|
112
112
|
|
|
113
113
|
A branch review report takes the other route and is swept rather than archived. `claude-review` writes it to `.canon/review/branch/`, the session addressing it reads it once, and the durable record of what a review found is the comment `claude-pr-review` posts on the pull request, so `claude-docs` deletes any report whose branch is gone. The body that writes a report owns how long it lives, which leaves the shipping branch's own report on disk through the run that cites it and collects it a branch later. What that loses is a local-only review on a branch that never opened a pull request, which is why the report says so where a reader meets it.
|
|
114
114
|
|
|
115
|
-
The plans sweep reads the whole board rather than the tasks the session touched. It is the one place the skill reaches past its own rule against editing a task file the session did not change, because a task that closed while an earlier run missed its archive is exactly what the sweep exists to clear. Reaching it is safe: the archive moves the plan and retargets the pointer in the same pass, so an untouched task ends up with a working link rather than a broken one.
|
|
116
|
-
|
|
117
115
|
`canon:claude-docs` decides which task closed by reading the diff rather than the conversation. It resolves a merge base against `origin/main`, unions the committed diff with the working tree and untracked files, then matches unchecked outcomes on the board against what shipped. A task that shipped without ever being discussed still gets marked. Requirements, architecture, and design stay session-sourced, because a diff cannot carry a judgment.
|
|
118
116
|
|
|
119
117
|
`.canon/tasks/` is gitignored and resolves at the main worktree root, so every session shares one board. One file per task is what keeps concurrent sessions from overwriting each other, since a gitignored board has no history to recover a lost write from. Its `index.md` is generated by a hook rather than by `bun run check`, because the whole-repo index walk skips gitignored folders.
|
|
@@ -126,7 +124,7 @@ Past that shape, it checks what a surviving row claims against what the tree hol
|
|
|
126
124
|
|
|
127
125
|
`canon:claude-tasks` owns the two operations that bracket a task's life. It creates the file, holding the filename convention and the frontmatter contract so a malformed write cannot break the index for every sibling, and it moves a shipped task to `.canon/tasks/archive/`. Creation is where the origin invariant is enforced: every task names a plan, a groundwork folder, an intake folder, or an issue, since a task with no origin is either lost context or work nobody decided to do.
|
|
128
126
|
|
|
129
|
-
Archiving a task
|
|
127
|
+
Archiving a task carries its plan with it, in the same act. The merge is what settles a plan, and the hook below reaches the archive with nobody watching, so a second call after it would be a second failure point leaving the task archived and the plan live. A task whose plan a sibling still cites archives on its own and leaves the plan live, since moving it on the first task to close strands every other pointer at a path that has gone.
|
|
130
128
|
|
|
131
129
|
Nothing chained that archive until the `post-merge` git hook landed. Every earlier step fires from `canon:claude-autoship` or `canon:git-ship`, both of which finish while the pull request is still open, so a task archived there would close for work that may be abandoned. The board is gitignored, which rules out reading it from anywhere but the machine that pulled. The hook names the board's archive candidates and stays silent otherwise, including on a project with no board.
|
|
132
130
|
|
|
@@ -24,6 +24,8 @@ The toolkit seed in `tooling/claude/seeds/.claude/DESIGN.md` ships a token-table
|
|
|
24
24
|
|
|
25
25
|
The `canon:claude-design-extract` skill drafts the file, sourcing tokens from a project's existing prose and CLI UI surfaces, or proposing them from `.claude/REQUIREMENTS.md` and a `## Personality` paragraph when no UI code exists yet. `canon design render` writes an HTML plus CSS preview to `.canon/review/design/` for eyeballing the current system without leaving Claude Code. See `.claude/context/design.md`.
|
|
26
26
|
|
|
27
|
+
A project wanting the toolkit's own values rather than its own runs `canon design install`, which copies one stylesheet to `.claude/design/base.css` carrying the token set as custom properties and two components built on them. That file is toolkit-owned and `canon design sync` refreshes it, so a project overrides a value in `.claude/design/project/` instead, which sync never touches. Nothing arrives without that install, and the two channels are independent: a record drafted by the extract skill is the project's own, and the installed stylesheet is the toolkit's.
|
|
28
|
+
|
|
27
29
|
A cell no source anchors ends in `? verify`, and the preview shows that marker beside the value rather than folding it in, so a swatch and a font sample stay built from the value alone. A confidence line above the sections names how many cells are anchored against how many are tagged, which is what tells a reader whether they are looking at a record of the code or a proposal about it. It reads the columns a source could anchor and leaves out the row names, so the ratio is not diluted by cells no tag could ever reach. The proposal path tags nearly all of them, so that count reads low on day one by design.
|
|
28
30
|
|
|
29
31
|
### Tools
|