@erclx/canon 4.123.0 → 4.125.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/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/auto-ship/SKILL.md +2 -4
- package/claude/skills/codebase-layout/REQUIREMENT.md +1 -1
- package/claude/skills/codebase-layout/SKILL.md +1 -1
- package/claude/skills/create-rule/SKILL.md +5 -2
- package/claude/skills/design-taste/SKILL.md +1 -1
- package/claude/skills/draft-ready/REQUIREMENT.md +1 -1
- package/claude/skills/memory-review/SKILL.md +1 -1
- package/claude/skills/memory-review/references/later-phases.md +1 -1
- package/claude/skills/memory-review/references/receipt-format.md +1 -1
- package/claude/skills/role-orchestrator/REQUIREMENT.md +2 -1
- package/claude/skills/role-orchestrator/SKILL.md +11 -9
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +2 -0
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +18 -3
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +4 -4
- package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +29 -0
- package/claude/skills/role-reviewer/REQUIREMENT.md +75 -0
- package/claude/skills/role-reviewer/SKILL.md +73 -0
- package/claude/skills/role-worker/SKILL.md +1 -1
- package/claude/skills/test-first/REQUIREMENT.md +1 -1
- package/docs/agents/counts.md +3 -3
- package/docs/agents/drift-surfaces.md +2 -2
- package/docs/agents/rule-citations.md +4 -4
- package/docs/agents/test-order.md +1 -1
- package/docs/index.md +3 -0
- package/docs/target-migrations.md +93 -0
- package/docs/target-projects.md +4 -216
- package/docs/target-scenarios.md +56 -0
- package/docs/target-sync.md +93 -0
- package/docs/workflow/ai-workflow.md +2 -201
- package/docs/workflow/index.md +2 -0
- package/docs/workflow/operating-model.md +28 -58
- package/docs/workflow/parallel-features.md +77 -0
- package/docs/workflow/skill-map.md +149 -0
- package/governance/renames.toml +34 -0
- package/governance/rules/{claude/560-diagrams.md → canon/660-diagrams.md} +1 -1
- package/governance/rules/{claude/562-session.md → canon/662-session.md} +1 -1
- package/governance/rules/code/000-code.md +21 -0
- package/governance/rules/lib/300-testing-ts.md +0 -4
- package/governance/rules/lib/330-testing-py.md +0 -4
- package/governance/rules/{claude/500-prose.md → writing/800-prose.md} +1 -1
- package/governance/rules/{claude/501-markdown.md → writing/801-markdown.md} +1 -1
- package/governance/rules/{claude/502-mermaid.md → writing/802-mermaid.md} +1 -1
- package/governance/stacks/base.toml +7 -6
- package/governance/stacks/node.toml +1 -1
- package/governance/stacks/python.toml +1 -1
- package/package.json +1 -1
- package/scripts/core/install-check.sh +3 -5
- package/src/claude/cases/workflow.ts +5 -0
- package/src/claude/routing.ts +1 -1
- package/src/commands/gov.ts +1 -1
- package/src/gate/measures.ts +1 -6
- package/src/gov/citations.ts +1 -1
- package/src/gov/list.ts +1 -1
- package/src/gov/restated.ts +2 -2
- package/src/init/steps.ts +1 -1
- package/src/labels/spelling.ts +1 -1
- package/src/records/prune.ts +1 -1
- package/src/shipped/references.ts +1 -1
- package/standards/rule.md +4 -6
- package/tooling/base/reference.md +1 -1
- package/tooling/claude/seeds/.claude/hooks/path-form.sh +1 -1
- package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +1 -1
- package/tooling/web/reference.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-review-fallback.md +0 -18
- package/governance/rules/core/000-constitution.md +0 -30
- package/governance/rules/core/020-concurrency.md +0 -22
- package/governance/rules/core/030-error-handling.md +0 -31
- package/governance/rules/core/040-performance.md +0 -20
- package/governance/rules/core/050-logging.md +0 -20
- package/governance/rules/core/060-naming.md +0 -19
- package/governance/rules/snippets/600-at-references.md +0 -9
- /package/governance/rules/{core/025-indexes.md → canon/601-indexes.md} +0 -0
- /package/governance/rules/{core/035-tasks.md → canon/602-tasks.md} +0 -0
- /package/governance/rules/{core/045-memory.md → canon/603-memory.md} +0 -0
- /package/governance/rules/{core/055-scratch.md → canon/604-scratch.md} +0 -0
- /package/governance/rules/{core/085-worktrees.md → canon/605-worktrees.md} +0 -0
- /package/governance/rules/{core/087-git.md → canon/606-git.md} +0 -0
- /package/governance/rules/{claude/510-context.md → canon/610-context.md} +0 -0
- /package/governance/rules/{claude/511-indexes.md → canon/611-indexes.md} +0 -0
- /package/governance/rules/{claude/520-wireframes.md → canon/620-wireframes.md} +0 -0
- /package/governance/rules/{claude/530-requirements.md → canon/630-requirements.md} +0 -0
- /package/governance/rules/{claude/540-architecture.md → canon/640-architecture.md} +0 -0
- /package/governance/rules/{claude/545-decisions.md → canon/645-decisions.md} +0 -0
- /package/governance/rules/{claude/550-design.md → canon/650-design.md} +0 -0
- /package/governance/rules/{claude/555-tasks.md → canon/655-tasks.md} +0 -0
- /package/governance/rules/{claude/556-groundwork.md → canon/656-groundwork.md} +0 -0
- /package/governance/rules/{claude/557-intake.md → canon/657-intake.md} +0 -0
- /package/governance/rules/{claude/558-plan.md → canon/658-plan.md} +0 -0
- /package/governance/rules/{claude/559-memory.md → canon/659-memory.md} +0 -0
- /package/governance/rules/{claude/561-teach.md → canon/661-teach.md} +0 -0
- /package/governance/rules/{claude/563-ready.md → canon/663-ready.md} +0 -0
- /package/governance/rules/{core/005-behavior.md → claude/565-behavior.md} +0 -0
- /package/governance/rules/{core/015-output.md → claude/566-output.md} +0 -0
- /package/governance/rules/{core/070-planning.md → claude/567-planning.md} +0 -0
- /package/governance/rules/{core → code}/010-testing.md +0 -0
- /package/governance/rules/{ci → tooling}/700-ci-workflow.md +0 -0
- /package/governance/rules/{core/065-spelling.md → tooling/765-spelling.md} +0 -0
- /package/governance/rules/{core/075-dependencies.md → tooling/775-dependencies.md} +0 -0
- /package/governance/rules/{core/080-config-comments.md → tooling/780-config-comments.md} +0 -0
- /package/governance/rules/{core/090-code-comments.md → tooling/790-code-comments.md} +0 -0
- /package/governance/rules/{claude/580-readme.md → writing/880-readme.md} +0 -0
- /package/governance/rules/{claude/591-standard-authoring.md → writing/891-standard-authoring.md} +0 -0
|
@@ -5,7 +5,7 @@ description: Reading where an implementation reached history ahead of its test,
|
|
|
5
5
|
|
|
6
6
|
# Test order
|
|
7
7
|
|
|
8
|
-
`canon gov test-order` reports where an implementation reached a commit ahead of the test covering the same behavior. It answers the rule in `.claude/rules/canon/
|
|
8
|
+
`canon gov test-order` reports where an implementation reached a commit ahead of the test covering the same behavior. It answers the rule in `.claude/rules/canon/claude/567-planning.md` that asks for the test first, which until this verb existed was prose loaded on a glob match with nothing measuring it.
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
canon gov test-order
|
package/docs/index.md
CHANGED
|
@@ -9,7 +9,10 @@ One-line reference for each doc in this folder.
|
|
|
9
9
|
|
|
10
10
|
## Agent surface
|
|
11
11
|
|
|
12
|
+
- [Target migrations](target-migrations.md): One-time moves a project scaffolded under an older toolkit runs before its next sync
|
|
12
13
|
- [Target projects](target-projects.md): Scaffold, add domains later, and sync upstream drift in a toolkit-managed project
|
|
14
|
+
- [Target scenarios](target-scenarios.md): Worked setups for a markdown-heavy project, a web application, and a monorepo
|
|
15
|
+
- [Target sync](target-sync.md): Check what drifted in a toolkit-managed project and pull it in, per domain or all at once
|
|
13
16
|
|
|
14
17
|
## Sub-catalogs
|
|
15
18
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Target migrations
|
|
3
|
+
description: One-time moves a project scaffolded under an older toolkit runs before its next sync
|
|
4
|
+
category: Agent surface
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Target migrations
|
|
8
|
+
|
|
9
|
+
Each section below is a move a project runs once, when it was scaffolded before the change the section names. Run `canon upgrade` first, so the verb doing the move is the current one. For the recurring sync that follows, see [target sync](target-sync.md).
|
|
10
|
+
|
|
11
|
+
## Move the records first, once
|
|
12
|
+
|
|
13
|
+
Session records moved out of `.claude/` and into a root of their own. What is committed stays where it is, and everything gitignored, being the task board, the plans, the memory pen, the review reports, and the scratch folder, now lives under `.canon/`, which a single ignore entry covers.
|
|
14
|
+
|
|
15
|
+
`4.7.0` carries `canon migrate records`, so run these three lines from inside the project. Run `canon upgrade` first regardless of what you hold, since the sweep learned to pass over the records themselves after that release and a `4.7.0` binary rewrites them.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
canon tooling sync claude . --write
|
|
19
|
+
canon migrate records --json
|
|
20
|
+
canon migrate records --write --json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The first line takes the `.canon/` ignore entry, and the verb refuses until the project has it, since every folder it relocates is ignored where it stands and landing one under a tracked root commits the memory pen. The second reports the plan and the third applies it, moving the folders and repointing every tracked file that cites one.
|
|
24
|
+
|
|
25
|
+
Order matters between the first line and the two under it. The sync prunes the twelve old `.claude/` ignore entries down to the one `.canon/` line, which leaves every record still at the old root visible to git and therefore to the verb. The sweep passes over them on purpose, reporting a count of what it left alone rather than reading the memory pen and the groundwork trails as source.
|
|
26
|
+
|
|
27
|
+
## Back records up off this machine, once
|
|
28
|
+
|
|
29
|
+
Moving records under `.canon/` relocates them, and relocating them is not the same as backing them up. Run `canon records push` once a private repository exists for them, and it prints the one-time setup command when it finds none, since the task board, the memory pen, and the groundwork trails otherwise live on one disk with nothing pushing them off it. `canon init` prints the same reminder as a notice-only step rather than trying to run this non-interactively, since the private repository does not exist yet at scaffold time.
|
|
30
|
+
|
|
31
|
+
Read the `ok` field out of the `--json` record rather than the exit code. A shell profile that wraps `canon` in a function takes its status from whatever the function runs last, so an absent subcommand and a clean run can both exit 0, and a reader watching the exit alone concludes the move happened.
|
|
32
|
+
|
|
33
|
+
A tracked file that names an old record path on purpose, such as prose dating a decision, keeps it by carrying `canon-keep-record-root` on that line or the nearest non-blank line above. The report pass prints every file it would rewrite, which is where to catch one before `--write` runs.
|
|
34
|
+
|
|
35
|
+
Until the move runs, the project is exposed. The shipped ignore set no longer names the old record paths, so a project holding records at `.claude/` stops ignoring them on its next `canon tooling sync`, and the first sign is a memory file or a task board appearing in a commit. Every command reads either root, so nothing else breaks in the meantime, and running the move is what closes it.
|
|
36
|
+
|
|
37
|
+
Then sweep the records themselves, which the three lines above never reach. They enumerate through git, and the records are gitignored by construction, so a task still naming its plan at the old root resolves nothing once the folders move.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
canon migrate record-tree --json
|
|
41
|
+
canon migrate record-tree --write --json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This is a separate step rather than a fourth line in the block above because the move has to land before there is a new root to walk. The scope is the folders a session still follows a path into, being `diagrams`, `memory`, `plans`, `proposals`, `review`, `tasks`, and `teach`, each minus its own `archive/` subtree. A closed groundwork or intake trail, the scratch folder, and the backup history are counted and left alone, since a path inside one of those sits in a sentence about work that already ended.
|
|
45
|
+
|
|
46
|
+
Read the report before `--write` here more carefully than above. The record tree is untracked, so a wrong rewrite has no git undo, and the report names every citation with its line number and the line text for exactly that reason. The same `canon-keep-record-root` marker protects a line that has to keep the old spelling.
|
|
47
|
+
|
|
48
|
+
## Move rules off the flat layout, once
|
|
49
|
+
|
|
50
|
+
Installed rules moved from a flat `.claude/rules/<subdir>/` layout to `.claude/rules/canon/<subdir>/`. A project that installed governance before that release still holds the flat layout, and neither of the two governance verbs below notices: `canon gov sync` reports `No governance surfaces found in target` and exits 0, and `canon gov install` lands a second, wrapped copy beside the stale one rather than replacing it.
|
|
51
|
+
|
|
52
|
+
Run `canon upgrade` first if you have not, then run this from inside the project:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
canon migrate rule-layout --json
|
|
56
|
+
canon migrate rule-layout --write --json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The first line reports the plan and the second applies it, relocating each rule and carrying an edited one's recorded hash forward so it still reports as edited on the next sync rather than reading clean. Run it once, ahead of `canon gov sync` or `canon gov install`, on any project scaffolded before this move landed.
|
|
60
|
+
|
|
61
|
+
## Rename the skill citations, once
|
|
62
|
+
|
|
63
|
+
Twenty-five plugin skills dropped their `claude-` prefix for two-word names, so `canon:claude-docs` answers as `canon:context-fold` and `canon:claude-tasks` as `canon:task-board`. A project that installed governance or tooling before that release holds files naming the old ones, and the plugin answers to none of them. A later release renamed `docs-fold` to `context-fold`, since the skill folds a session into the context entries and the task board rather than into `docs/`, and the same sweep carries a project across that rename too. <!-- canon-keep-retired -->
|
|
64
|
+
|
|
65
|
+
Two of those files run rather than sit there. `.husky/post-merge` prints a command for a person to type, and `.claude/hooks/pr-create-log.sh` hands a session a message naming a skill, so a stale copy tells someone to invoke something that no longer exists. A rule under `.claude/rules/canon/core/` names skills too, though a rule is read rather than run.
|
|
66
|
+
|
|
67
|
+
Resync what the toolkit owns, then sweep what the project wrote:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
canon gov sync
|
|
71
|
+
canon tooling sync <stack> . --write
|
|
72
|
+
canon migrate skill-names --json
|
|
73
|
+
canon migrate skill-names --write --json
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Run the second line once per stack the project holds, since the two hooks above arrive from different ones. The third reports the plan and the fourth applies it.
|
|
77
|
+
|
|
78
|
+
The order carries the reason. A sync replaces each toolkit-owned copy with one already carrying the new names, and the sweep afterwards reaches the prose the project wrote itself. Sweeping first rewrites those installed files in place, which moves them off the hash the install recorded, so the next sync reads them as edited by the project and leaves them alone.
|
|
79
|
+
|
|
80
|
+
## Move the surfaces off `.claude/`, once
|
|
81
|
+
|
|
82
|
+
Tracked surfaces moved out of `.claude/` and into a root of their own. `context/`, `decisions/`, `wireframes/`, the three loose documents (`ARCHITECTURE.md`, `REQUIREMENTS.md`, `DESIGN.md`), and the install stamp folder, respelled `canon/config/`, now live under `canon/`, and `rules/`, `skills/`, `hooks/`, and `settings.json` stay put since Claude Code reads those by path.
|
|
83
|
+
|
|
84
|
+
Run `canon upgrade` first if you have not, then run this from inside the project:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
canon migrate surface-roots --json
|
|
88
|
+
canon migrate surface-roots --write --json
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The first line reports the plan and the second applies it, moving each surface with `git mv` so its history follows and repointing every tracked citation of one in the same run. See `canon docs agents` for the full file list rather than reading it here.
|
|
92
|
+
|
|
93
|
+
Neither `canon sync --check` nor any other command notices a surface still sitting at `.claude/`, so running this is on you rather than on a prompt from the toolkit.
|
package/docs/target-projects.md
CHANGED
|
@@ -84,7 +84,7 @@ Each diagram entry records the commit and date it was last verified against, and
|
|
|
84
84
|
|
|
85
85
|
### Stack decision
|
|
86
86
|
|
|
87
|
-
The default path is `base`. `canon init` on `base` installs base tooling configs, Claude seeds, and governance
|
|
87
|
+
The default path is `base`. `canon init` on `base` installs base tooling configs, Claude seeds, and the `canon`, `claude`, `tooling`, and `writing` governance rule groups, and scaffolds an empty `.claude/wiki/`. Most projects need nothing more.
|
|
88
88
|
|
|
89
89
|
Escalate only for real web apps. The `target-setup` skill reads `package.json` and the framework configs, then picks the matching tooling stack (`canon tooling list --json` names the current set) and the matching governance stack (`react`, `nextjs`, `astro`, `node`). A Next.js app lands on `nextjs`, which extends `react` with the App Router rule a Vite React app does not get.
|
|
90
90
|
|
|
@@ -127,228 +127,16 @@ Per-domain mechanics live in the corresponding `docs/<domain>.md`. The skill bod
|
|
|
127
127
|
|
|
128
128
|
## Sync upstream drift
|
|
129
129
|
|
|
130
|
-
When the toolkit updates, target
|
|
131
|
-
|
|
132
|
-
### Move the records first, once
|
|
133
|
-
|
|
134
|
-
Session records moved out of `.claude/` and into a root of their own. What is committed stays where it is, and everything gitignored, being the task board, the plans, the memory pen, the review reports, and the scratch folder, now lives under `.canon/`, which a single ignore entry covers.
|
|
135
|
-
|
|
136
|
-
`4.7.0` carries `canon migrate records`, so run these three lines from inside the project. Run `canon upgrade` first regardless of what you hold, since the sweep learned to pass over the records themselves after that release and a `4.7.0` binary rewrites them.
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
canon tooling sync claude . --write
|
|
140
|
-
canon migrate records --json
|
|
141
|
-
canon migrate records --write --json
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
The first line takes the `.canon/` ignore entry, and the verb refuses until the project has it, since every folder it relocates is ignored where it stands and landing one under a tracked root commits the memory pen. The second reports the plan and the third applies it, moving the folders and repointing every tracked file that cites one.
|
|
145
|
-
|
|
146
|
-
Order matters between the first line and the two under it. The sync prunes the twelve old `.claude/` ignore entries down to the one `.canon/` line, which leaves every record still at the old root visible to git and therefore to the verb. The sweep passes over them on purpose, reporting a count of what it left alone rather than reading the memory pen and the groundwork trails as source.
|
|
147
|
-
|
|
148
|
-
### Back records up off this machine, once
|
|
149
|
-
|
|
150
|
-
Moving records under `.canon/` relocates them, and relocating them is not the same as backing them up. Run `canon records push` once a private repository exists for them, and it prints the one-time setup command when it finds none, since the task board, the memory pen, and the groundwork trails otherwise live on one disk with nothing pushing them off it. `canon init` prints the same reminder as a notice-only step rather than trying to run this non-interactively, since the private repository does not exist yet at scaffold time.
|
|
151
|
-
|
|
152
|
-
Read the `ok` field out of the `--json` record rather than the exit code. A shell profile that wraps `canon` in a function takes its status from whatever the function runs last, so an absent subcommand and a clean run can both exit 0, and a reader watching the exit alone concludes the move happened.
|
|
153
|
-
|
|
154
|
-
A tracked file that names an old record path on purpose, such as prose dating a decision, keeps it by carrying `canon-keep-record-root` on that line or the nearest non-blank line above. The report pass prints every file it would rewrite, which is where to catch one before `--write` runs.
|
|
155
|
-
|
|
156
|
-
Until the move runs, the project is exposed. The shipped ignore set no longer names the old record paths, so a project holding records at `.claude/` stops ignoring them on its next `canon tooling sync`, and the first sign is a memory file or a task board appearing in a commit. Every command reads either root, so nothing else breaks in the meantime, and running the move is what closes it.
|
|
157
|
-
|
|
158
|
-
Then sweep the records themselves, which the three lines above never reach. They enumerate through git, and the records are gitignored by construction, so a task still naming its plan at the old root resolves nothing once the folders move.
|
|
159
|
-
|
|
160
|
-
```bash
|
|
161
|
-
canon migrate record-tree --json
|
|
162
|
-
canon migrate record-tree --write --json
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
This is a separate step rather than a fourth line in the block above because the move has to land before there is a new root to walk. The scope is the folders a session still follows a path into, being `diagrams`, `memory`, `plans`, `proposals`, `review`, `tasks`, and `teach`, each minus its own `archive/` subtree. A closed groundwork or intake trail, the scratch folder, and the backup history are counted and left alone, since a path inside one of those sits in a sentence about work that already ended.
|
|
166
|
-
|
|
167
|
-
Read the report before `--write` here more carefully than above. The record tree is untracked, so a wrong rewrite has no git undo, and the report names every citation with its line number and the line text for exactly that reason. The same `canon-keep-record-root` marker protects a line that has to keep the old spelling.
|
|
168
|
-
|
|
169
|
-
### Move rules off the flat layout, once
|
|
170
|
-
|
|
171
|
-
Installed rules moved from a flat `.claude/rules/<subdir>/` layout to `.claude/rules/canon/<subdir>/`. A project that installed governance before that release still holds the flat layout, and neither of the two governance verbs below notices: `canon gov sync` reports `No governance surfaces found in target` and exits 0, and `canon gov install` lands a second, wrapped copy beside the stale one rather than replacing it.
|
|
172
|
-
|
|
173
|
-
Run `canon upgrade` first if you have not, then run this from inside the project:
|
|
174
|
-
|
|
175
|
-
```bash
|
|
176
|
-
canon migrate rule-layout --json
|
|
177
|
-
canon migrate rule-layout --write --json
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
The first line reports the plan and the second applies it, relocating each rule and carrying an edited one's recorded hash forward so it still reports as edited on the next sync rather than reading clean. Run it once, ahead of `canon gov sync` or `canon gov install`, on any project scaffolded before this move landed.
|
|
181
|
-
|
|
182
|
-
### Rename the skill citations, once
|
|
183
|
-
|
|
184
|
-
Twenty-five plugin skills dropped their `claude-` prefix for two-word names, so `canon:claude-docs` answers as `canon:context-fold` and `canon:claude-tasks` as `canon:task-board`. A project that installed governance or tooling before that release holds files naming the old ones, and the plugin answers to none of them. A later release renamed `docs-fold` to `context-fold`, since the skill folds a session into the context entries and the task board rather than into `docs/`, and the same sweep carries a project across that rename too. <!-- canon-keep-retired -->
|
|
185
|
-
|
|
186
|
-
Two of those files run rather than sit there. `.husky/post-merge` prints a command for a person to type, and `.claude/hooks/pr-create-log.sh` hands a session a message naming a skill, so a stale copy tells someone to invoke something that no longer exists. A rule under `.claude/rules/canon/core/` names skills too, though a rule is read rather than run.
|
|
187
|
-
|
|
188
|
-
Resync what the toolkit owns, then sweep what the project wrote:
|
|
189
|
-
|
|
190
|
-
```bash
|
|
191
|
-
canon gov sync
|
|
192
|
-
canon tooling sync <stack> . --write
|
|
193
|
-
canon migrate skill-names --json
|
|
194
|
-
canon migrate skill-names --write --json
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
Run the second line once per stack the project holds, since the two hooks above arrive from different ones. The third reports the plan and the fourth applies it.
|
|
198
|
-
|
|
199
|
-
The order carries the reason. A sync replaces each toolkit-owned copy with one already carrying the new names, and the sweep afterwards reaches the prose the project wrote itself. Sweeping first rewrites those installed files in place, which moves them off the hash the install recorded, so the next sync reads them as edited by the project and leaves them alone.
|
|
200
|
-
|
|
201
|
-
### Move the surfaces off `.claude/`, once
|
|
202
|
-
|
|
203
|
-
Tracked surfaces moved out of `.claude/` and into a root of their own. `context/`, `decisions/`, `wireframes/`, the three loose documents (`ARCHITECTURE.md`, `REQUIREMENTS.md`, `DESIGN.md`), and the install stamp folder, respelled `canon/config/`, now live under `canon/`, and `rules/`, `skills/`, `hooks/`, and `settings.json` stay put since Claude Code reads those by path.
|
|
204
|
-
|
|
205
|
-
Run `canon upgrade` first if you have not, then run this from inside the project:
|
|
206
|
-
|
|
207
|
-
```bash
|
|
208
|
-
canon migrate surface-roots --json
|
|
209
|
-
canon migrate surface-roots --write --json
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
The first line reports the plan and the second applies it, moving each surface with `git mv` so its history follows and repointing every tracked citation of one in the same run. See `canon docs agents` for the full file list rather than reading it here.
|
|
213
|
-
|
|
214
|
-
Neither `canon sync --check` nor any other command notices a surface still sitting at `.claude/`, so running this is on you rather than on a prompt from the toolkit.
|
|
215
|
-
|
|
216
|
-
### Check first
|
|
217
|
-
|
|
218
|
-
`canon sync --check <path>` reports what has drifted without writing anything. It splits each difference by cause, which is the question that decides what to do next.
|
|
219
|
-
|
|
220
|
-
`canon:target-check` is the same reading grouped by domain rather than by cause, run from inside the project. It reports the toolkit version, the `canon/` folder, governance rules, tooling, and seeds from the verbs that own each comparison, and reports the Claude harness as unchecked because no verb reads it. It repairs nothing, so it is what to run before deciding rather than instead of deciding.
|
|
221
|
-
|
|
222
|
-
#### The binary first
|
|
223
|
-
|
|
224
|
-
The report opens by naming the binary running it. The installed version reads against the newest published one, and a version behind that points you at `canon upgrade`, since every section under it is a reading taken by whichever toolkit you happen to have. An unreachable registry reports unknown with its reason rather than failing, so `--exit-code` still gates on the drift the check measured locally and an offline machine stays green.
|
|
225
|
-
|
|
226
|
-
#### Then the causes
|
|
227
|
-
|
|
228
|
-
A `stale` file still matches what the toolkit installed, so the update is mechanical. A `customized` file carries local edits, so taking the upstream version is a decision and `canon:seed-sync` is the tool for it. A `stranded` file sits where an older toolkit installed it and the toolkit has since moved, which is a relocation the report names but no command runs. A `retired` rule is one under `.claude/rules/canon/` the toolkit no longer ships, and the next `canon gov sync` deletes it. A `renamed` rule is one the toolkit now ships under a new name, and the next sync installs the new name and deletes the old file.
|
|
229
|
-
|
|
230
|
-
That attribution comes from `canon/config/config.json`, which every install and sync now writes, falling back to `.claude/canon/config.json` for a target stamped before that move. A target stamped before that path shipped is read from the retired `.claude/canon.json` instead, reported rather than migrated. Governance records a hash per installed file, plus the stack `canon gov install` was given, and tooling records the stack chain it resolved instead of any file hash, since its install runs no per-file walk to attribute.
|
|
231
|
-
|
|
232
|
-
Each domain holds its own toolkit commit, so syncing governance today does not move the revision tooling measures against, and each domain reports the upstream commits touching its own source path. Running any sync stamps that domain, and the report names the ones still unstamped.
|
|
233
|
-
|
|
234
|
-
A project that has never synced under a toolkit new enough to write a stamp falls back to the toolkit's own git history. Installed content matching any version that history published proves the file untouched, so it reports `stale` naming the commit it came from, and content matching no published version stays `drifted`. That fallback needs the toolkit as a git checkout. Installed from the registry it ships source without history, and the report says attribution was unavailable rather than reading every file as a local edit.
|
|
235
|
-
|
|
236
|
-
Further causes sit outside the per-domain scan, each naming something that walk cannot see. A seed the project edited is reported under `seeds` and reconciled with `canon:seed-sync`, since no sync command touches a seed. A file a newer seed folder replaced is reported under `superseded`, such as `.claude/TASKS.md` against the `.canon/tasks/` that now ships, and nothing moves it because the content is the project's own. A domain sitting at the root layout with nothing under `.claude/` is reported under `unmigrated`, and no command moves it either. The project moves the content itself.
|
|
237
|
-
|
|
238
|
-
`unmigrated` currently names no domain, since standards and snippets are the two the toolkit ever installed at the project root and both closed their install channel. A target still holding a root `standards/` or `snippets/` folder from an older toolkit is carrying its own authoring surface now, not an unfinished install, and nothing proposes moving either.
|
|
239
|
-
|
|
240
|
-
#### Rules you never received
|
|
241
|
-
|
|
242
|
-
A sync refreshes the files you already hold and adds none, so your rule set is frozen at the date you installed governance while every file in it reports as current. `newRules` names a rule you could receive that your tree does not hold, which is the one section that reads your absence rather than your contents. `canon gov sync` reports the same rules per file, marked `missing`, so you see them either way you check.
|
|
243
|
-
|
|
244
|
-
Take a clean section as reporting rather than as delivering. Nothing here installs, and it counts toward nothing, so pick the rules up with `canon gov install <stack>` or take one with `--add <rule>`. That separation is deliberate: adopting a rule changes what your project is governed by, which is a choice a sync has no business making for you.
|
|
245
|
-
|
|
246
|
-
Since `canon gov install` records the stack you gave it, the list is read by comparing that stack's current rules against what you hold right now, with no date involved at all. That is what lets it name a rule the toolkit shipped before you last synced: nothing here depends on when you installed. A target stamped before this recording shipped falls back to the older read below.
|
|
247
|
-
|
|
248
|
-
The fallback filters to what your stack can receive, read off the rule folders you already carry plus the folders the base stack takes whole. A rule under `lang/` or `ui/` belongs to some stacks and not others, so an unfiltered list would name rules you can never install. A target with neither a recorded stack nor a governance anchor reports nothing at all, since there is no date to measure against either.
|
|
249
|
-
|
|
250
|
-
In the fallback, an empty section is not proof either way when the toolkit running the check is not a full git clone. The read needs history to reach your anchor, and an install from the registry ships none, so the section goes quiet rather than saying it could not measure. Run the check from a clone before reading a clean result as a complete one. The stack-based read above is untouched by this, since it consults no history.
|
|
251
|
-
|
|
252
|
-
This also closes the case where a rule arrives citing a sibling you do not have. A sync refreshing a rule can land a version pointing at a file it never installs, and the section names the missing sibling rather than the broken citation, which is enough to act on.
|
|
253
|
-
|
|
254
|
-
#### What the toolkit stopped shipping
|
|
255
|
-
|
|
256
|
-
The last cause runs backwards. Every one above starts from what the toolkit ships and asks whether the target matches, so a folder the toolkit dropped appears in none of them. `reverse` walks the target instead and reports a folder sitting at a top-level path the toolkit once shipped and has since deleted.
|
|
257
|
-
|
|
258
|
-
Each entry carries a verdict, since a dropped folder and one the project wrote are the same bytes at the same path.
|
|
259
|
-
|
|
260
|
-
- `dropped` names the commit that published the content
|
|
261
|
-
- `unattributed` means the toolkit shipped that path and the content matches no version it published
|
|
262
|
-
- `project` means the folder only shares a retired name
|
|
263
|
-
|
|
264
|
-
Nothing acts on any of them, and the verdict is what makes the list safe to read.
|
|
265
|
-
|
|
266
|
-
Add `--json` for the machine-readable report, and `--exit-code` to fail a CI job when a target falls behind. Files the project authored itself never count toward that exit code, and neither do superseded artifacts, seed drift, tooling, or anything the reverse walk reports, since each names content the project is expected to edit or place itself. An unmigrated domain does count, because running the relocation closes it.
|
|
267
|
-
|
|
268
|
-
Tooling reports under a section of its own, and `measured` there says whether the target ever recorded a chain. One that never ran a tooling sync reports unmeasured rather than clean, which is what separates tooling nobody has looked at from tooling that is current. A workspace root records nothing either way, since each package resolves its own chain.
|
|
269
|
-
|
|
270
|
-
Reconcile the configs with `canon tooling diff <stack> <path>` to read which files differ, then run `canon tooling sync <stack> <path> --write` to apply them. The drift report counts categories and the diff names paths, which is the difference worth knowing before a golden config the project edited is replaced.
|
|
271
|
-
|
|
272
|
-
### Catch-all
|
|
273
|
-
|
|
274
|
-
`canon sync <path>` runs every installed domain's sync in sequence. Safe to run on a cadence.
|
|
275
|
-
|
|
276
|
-
It never touches user-owned seed files. Governance rules in `.claude/rules/` and tooling configs refresh in place. Stale `.claude/GOV.md` from earlier installs is removed.
|
|
277
|
-
|
|
278
|
-
Standards take no part in that run. Nothing installed them, so there is no copy to reconcile and no `canon standards sync` to reach for.
|
|
279
|
-
|
|
280
|
-
### Targeted
|
|
281
|
-
|
|
282
|
-
- Claude seed docs such as `CLAUDE.md` and `canon/REQUIREMENTS.md`: invoke `canon:seed-sync`. The skill splits each file into a preamble (between the H1 and the first H2) plus one part per `##` section, then diffs part by part and proposes per-part edits. User customizations are preserved.
|
|
283
|
-
- Governance rules already installed: `canon gov sync <path>` diffs and applies, and never adds new rules. A rule your recorded stack lists reports as `missing` instead.
|
|
284
|
-
- Tooling configs and seeds: `canon tooling <stack> <path>` overwrites golden configs and merges seeds
|
|
285
|
-
- Reference docs for a stack: `canon tooling reference <stack>` reads and never writes, so there is nothing to sync
|
|
286
|
-
- Index regeneration after markdown edits: `canon indexes regen`
|
|
287
|
-
|
|
288
|
-
Use a targeted entry point when only one surface moved upstream. Use the catch-all when the toolkit lands a bundled release.
|
|
289
|
-
|
|
290
|
-
## Verify a sync
|
|
291
|
-
|
|
292
|
-
Before running a sync against a real project, run the relevant sandbox scenario. The sandbox provisions a representative project state and routes `SANDBOX_SCENARIO=sync` through the domain flow. See [sandbox](../canon/context/sandbox/index.md) for the scenario catalog and routing patterns.
|
|
130
|
+
When the toolkit updates, a target pulls changes per domain. A project scaffolded before a layout move runs the one-time moves in [target migrations](target-migrations.md) first. [Target sync](target-sync.md) then covers reading what drifted, the catch-all and targeted entry points, verifying a sync in the sandbox, and running one from an agent session.
|
|
293
131
|
|
|
294
132
|
## Scenarios
|
|
295
133
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
```bash
|
|
299
|
-
cd <your-project>
|
|
300
|
-
claude
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
In the session, invoke `canon:target-setup`. The skill detects no framework and resolves tooling to `base` and governance to `base`. The preview marks both stacks as fallbacks, since neither came from a match, then the chain runs `canon init`.
|
|
304
|
-
|
|
305
|
-
Ongoing: run `canon sync --check .` to see what has drifted, then invoke `canon:seed-sync` for seed drift or `canon sync .` for a catch-all refresh.
|
|
306
|
-
|
|
307
|
-
### Web application
|
|
308
|
-
|
|
309
|
-
```bash
|
|
310
|
-
bun create vite my-app && cd my-app
|
|
311
|
-
claude
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
Invoke `canon:target-setup`. The skill reads `package.json` and the Vite config, resolves tooling to `vite-react` and governance to `react`, and runs `canon init` with the resolved flags.
|
|
315
|
-
|
|
316
|
-
Ongoing maintenance:
|
|
317
|
-
|
|
318
|
-
- What has drifted: `canon sync --check .`
|
|
319
|
-
- Seed drift: invoke `canon:seed-sync`
|
|
320
|
-
- Catch-all sync: `canon sync .`
|
|
321
|
-
- Governance rule refresh only: `canon gov sync .`
|
|
322
|
-
- Layer a new rule on top, for example `260-shadcn` after adopting shadcn: `canon gov install react --add 260-shadcn .`
|
|
323
|
-
|
|
324
|
-
### Monorepo with multiple language roots
|
|
325
|
-
|
|
326
|
-
The repo root owns the shared `base` layer, and each language lives in its own subfolder.
|
|
327
|
-
|
|
328
|
-
```bash
|
|
329
|
-
canon init --stack react .
|
|
330
|
-
canon tooling sync vite-react ./frontend --skip base --write
|
|
331
|
-
canon tooling sync python ./backend --skip base --write
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
`--skip base` drops the `base` layer from each subtree sync, so husky, prettier, cspell, commitlint, and CI stay single at the repo root. Without it, every subtree re-drops husky, and since git honors only one `core.hooksPath` the extra hook dirs silently break. Each subtree still gets its own framework configs (eslint, vitest, tsconfig, vite), and its own stack reference reads through `canon tooling reference <stack>`.
|
|
335
|
-
|
|
336
|
-
A sync whose target sits below the git root knows it is writing into a subfolder:
|
|
337
|
-
|
|
338
|
-
- It withholds `.github/`, since GitHub reads workflows only at the repository root, and names each withheld file. Run the subtree from a job in the root workflow instead, with `working-directory` set to the subtree path the sync prints, such as `working-directory: frontend`.
|
|
339
|
-
- It writes a nested `cspell.json` that registers the subtree's word lists, so a root `cspell '**'` reads them for files under that folder. It leaves a spell config the subtree already has alone.
|
|
340
|
-
- It installs no dependencies and adds no scripts to a subtree without a `package.json`. It names what it skipped and ends on `run 'bun init' in <path>, then sync again`.
|
|
341
|
-
- The subtree's `scripts/verify.sh` skips the format, spell, and shell phases a subtree does not declare, since the root runs them, and still fails on a missing lint, typecheck, or test script.
|
|
342
|
-
|
|
343
|
-
## Running sync from an agent session
|
|
344
|
-
|
|
345
|
-
`canon sync .` applies every installed domain sync, then offers to commit the result and open a pull request. That last step needs a terminal. Under `CANON_NON_INTERACTIVE=1`, which is how an agent runs it, the domain syncs still apply and the git workflow is refused: the command reports the branch and commit it would have created, writes nothing to git, and exits 0. Review the working tree and commit it yourself, or rerun interactively to reach the commit and pull request options.
|
|
346
|
-
|
|
347
|
-
Sync also refuses a target whose working tree is dirty, so commit or stash before running it. `canon sync --check .` has neither restriction, since it writes nothing.
|
|
134
|
+
[Target scenarios](target-scenarios.md) walks a markdown-heavy project, a web application, and a monorepo with several language roots from the first session through ongoing maintenance.
|
|
348
135
|
|
|
349
136
|
## Related
|
|
350
137
|
|
|
351
138
|
- [agents](agents/index.md): CLI flags, exit codes, and JSON output shapes
|
|
139
|
+
- [target migrations](target-migrations.md), [target sync](target-sync.md), [target scenarios](target-scenarios.md): the one-time moves, the recurring sync, and worked setups
|
|
352
140
|
- [AI workflow](workflow/ai-workflow.md): feature-development loop inside a toolkit-managed project
|
|
353
141
|
- [tooling](../canon/context/tooling/index.md), [governance](../canon/context/governance/index.md), [claude plugin](../canon/context/claude-plugin/index.md), [indexes](../canon/context/indexes.md), [snippets](../canon/context/snippets.md), [standards](../canon/context/standards/index.md): per-domain mechanics
|
|
354
142
|
- [sandbox](../canon/context/sandbox/index.md): scenario catalog for verifying domain flows
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Target scenarios
|
|
3
|
+
description: Worked setups for a markdown-heavy project, a web application, and a monorepo
|
|
4
|
+
category: Agent surface
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Target scenarios
|
|
8
|
+
|
|
9
|
+
Three worked setups, each from the first session through ongoing maintenance. [Target projects](target-projects.md) carries the lifecycle they walk.
|
|
10
|
+
|
|
11
|
+
## Markdown-heavy project
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cd <your-project>
|
|
15
|
+
claude
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
In the session, invoke `canon:target-setup`. The skill detects no framework and resolves tooling to `base` and governance to `base`. The preview marks both stacks as fallbacks, since neither came from a match, then the chain runs `canon init`.
|
|
19
|
+
|
|
20
|
+
Ongoing: run `canon sync --check .` to see what has drifted, then invoke `canon:seed-sync` for seed drift or `canon sync .` for a catch-all refresh.
|
|
21
|
+
|
|
22
|
+
## Web application
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bun create vite my-app && cd my-app
|
|
26
|
+
claude
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Invoke `canon:target-setup`. The skill reads `package.json` and the Vite config, resolves tooling to `vite-react` and governance to `react`, and runs `canon init` with the resolved flags.
|
|
30
|
+
|
|
31
|
+
Ongoing maintenance:
|
|
32
|
+
|
|
33
|
+
- What has drifted: `canon sync --check .`
|
|
34
|
+
- Seed drift: invoke `canon:seed-sync`
|
|
35
|
+
- Catch-all sync: `canon sync .`
|
|
36
|
+
- Governance rule refresh only: `canon gov sync .`
|
|
37
|
+
- Layer a new rule on top, for example `260-shadcn` after adopting shadcn: `canon gov install react --add 260-shadcn .`
|
|
38
|
+
|
|
39
|
+
## Monorepo with multiple language roots
|
|
40
|
+
|
|
41
|
+
The repo root owns the shared `base` layer, and each language lives in its own subfolder.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
canon init --stack react .
|
|
45
|
+
canon tooling sync vite-react ./frontend --skip base --write
|
|
46
|
+
canon tooling sync python ./backend --skip base --write
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`--skip base` drops the `base` layer from each subtree sync, so husky, prettier, cspell, commitlint, and CI stay single at the repo root. Without it, every subtree re-drops husky, and since git honors only one `core.hooksPath` the extra hook dirs silently break. Each subtree still gets its own framework configs (eslint, vitest, tsconfig, vite), and its own stack reference reads through `canon tooling reference <stack>`.
|
|
50
|
+
|
|
51
|
+
A sync whose target sits below the git root knows it is writing into a subfolder:
|
|
52
|
+
|
|
53
|
+
- It withholds `.github/`, since GitHub reads workflows only at the repository root, and names each withheld file. Run the subtree from a job in the root workflow instead, with `working-directory` set to the subtree path the sync prints, such as `working-directory: frontend`.
|
|
54
|
+
- It writes a nested `cspell.json` that registers the subtree's word lists, so a root `cspell '**'` reads them for files under that folder. It leaves a spell config the subtree already has alone.
|
|
55
|
+
- It installs no dependencies and adds no scripts to a subtree without a `package.json`. It names what it skipped and ends on `run 'bun init' in <path>, then sync again`.
|
|
56
|
+
- The subtree's `scripts/verify.sh` skips the format, spell, and shell phases a subtree does not declare, since the root runs them, and still fails on a missing lint, typecheck, or test script.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Target sync
|
|
3
|
+
description: Check what drifted in a toolkit-managed project and pull it in, per domain or all at once
|
|
4
|
+
category: Agent surface
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Target sync
|
|
8
|
+
|
|
9
|
+
When the toolkit updates, a target project pulls changes per domain. There is one catch-all and several targeted entry points. A project scaffolded before a layout move runs the one-time moves in [target migrations](target-migrations.md) first.
|
|
10
|
+
|
|
11
|
+
## Check first
|
|
12
|
+
|
|
13
|
+
`canon sync --check <path>` reports what has drifted without writing anything. It splits each difference by cause, which is the question that decides what to do next.
|
|
14
|
+
|
|
15
|
+
`canon:target-check` is the same reading grouped by domain rather than by cause, run from inside the project. It reports the toolkit version, the `canon/` folder, governance rules, tooling, and seeds from the verbs that own each comparison, and reports the Claude harness as unchecked because no verb reads it. It repairs nothing, so it is what to run before deciding rather than instead of deciding.
|
|
16
|
+
|
|
17
|
+
### The binary first
|
|
18
|
+
|
|
19
|
+
The report opens by naming the binary running it. The installed version reads against the newest published one, and a version behind that points you at `canon upgrade`, since every section under it is a reading taken by whichever toolkit you happen to have. An unreachable registry reports unknown with its reason rather than failing, so `--exit-code` still gates on the drift the check measured locally and an offline machine stays green.
|
|
20
|
+
|
|
21
|
+
### Then the causes
|
|
22
|
+
|
|
23
|
+
A `stale` file still matches what the toolkit installed, so the update is mechanical. A `customized` file carries local edits, so taking the upstream version is a decision and `canon:seed-sync` is the tool for it. A `stranded` file sits where an older toolkit installed it and the toolkit has since moved, which is a relocation the report names but no command runs. A `retired` rule is one under `.claude/rules/canon/` the toolkit no longer ships, and the next `canon gov sync` deletes it. A `renamed` rule is one the toolkit now ships under a new name, and the next sync installs the new name and deletes the old file.
|
|
24
|
+
|
|
25
|
+
That attribution comes from `canon/config/config.json`, which every install and sync now writes, falling back to `.claude/canon/config.json` for a target stamped before that move. A target stamped before that path shipped is read from the retired `.claude/canon.json` instead, reported rather than migrated. Governance records a hash per installed file, plus the stack `canon gov install` was given, and tooling records the stack chain it resolved instead of any file hash, since its install runs no per-file walk to attribute.
|
|
26
|
+
|
|
27
|
+
Each domain holds its own toolkit commit, so syncing governance today does not move the revision tooling measures against, and each domain reports the upstream commits touching its own source path. Running any sync stamps that domain, and the report names the ones still unstamped.
|
|
28
|
+
|
|
29
|
+
A project that has never synced under a toolkit new enough to write a stamp falls back to the toolkit's own git history. Installed content matching any version that history published proves the file untouched, so it reports `stale` naming the commit it came from, and content matching no published version stays `drifted`. That fallback needs the toolkit as a git checkout. Installed from the registry it ships source without history, and the report says attribution was unavailable rather than reading every file as a local edit.
|
|
30
|
+
|
|
31
|
+
Further causes sit outside the per-domain scan, each naming something that walk cannot see. A seed the project edited is reported under `seeds` and reconciled with `canon:seed-sync`, since no sync command touches a seed. A file a newer seed folder replaced is reported under `superseded`, such as `.claude/TASKS.md` against the `.canon/tasks/` that now ships, and nothing moves it because the content is the project's own. A domain sitting at the root layout with nothing under `.claude/` is reported under `unmigrated`, and no command moves it either. The project moves the content itself.
|
|
32
|
+
|
|
33
|
+
`unmigrated` currently names no domain, since standards and snippets are the two the toolkit ever installed at the project root and both closed their install channel. A target still holding a root `standards/` or `snippets/` folder from an older toolkit is carrying its own authoring surface now, not an unfinished install, and nothing proposes moving either.
|
|
34
|
+
|
|
35
|
+
### Rules you never received
|
|
36
|
+
|
|
37
|
+
A sync refreshes the files you already hold and adds none, so your rule set is frozen at the date you installed governance while every file in it reports as current. `newRules` names a rule you could receive that your tree does not hold, which is the one section that reads your absence rather than your contents. `canon gov sync` reports the same rules per file, marked `missing`, so you see them either way you check.
|
|
38
|
+
|
|
39
|
+
Take a clean section as reporting rather than as delivering. Nothing here installs, and it counts toward nothing, so pick the rules up with `canon gov install <stack>` or take one with `--add <rule>`. That separation is deliberate: adopting a rule changes what your project is governed by, which is a choice a sync has no business making for you.
|
|
40
|
+
|
|
41
|
+
Since `canon gov install` records the stack you gave it, the list is read by comparing that stack's current rules against what you hold right now, with no date involved at all. That is what lets it name a rule the toolkit shipped before you last synced: nothing here depends on when you installed. A target stamped before this recording shipped falls back to the older read below.
|
|
42
|
+
|
|
43
|
+
The fallback filters to what your stack can receive, read off the rule folders you already carry plus the folders the base stack takes whole. A rule under `lang/` or `ui/` belongs to some stacks and not others, so an unfiltered list would name rules you can never install. A target with neither a recorded stack nor a governance anchor reports nothing at all, since there is no date to measure against either.
|
|
44
|
+
|
|
45
|
+
In the fallback, an empty section is not proof either way when the toolkit running the check is not a full git clone. The read needs history to reach your anchor, and an install from the registry ships none, so the section goes quiet rather than saying it could not measure. Run the check from a clone before reading a clean result as a complete one. The stack-based read above is untouched by this, since it consults no history.
|
|
46
|
+
|
|
47
|
+
This also closes the case where a rule arrives citing a sibling you do not have. A sync refreshing a rule can land a version pointing at a file it never installs, and the section names the missing sibling rather than the broken citation, which is enough to act on.
|
|
48
|
+
|
|
49
|
+
### What the toolkit stopped shipping
|
|
50
|
+
|
|
51
|
+
The last cause runs backwards. Every one above starts from what the toolkit ships and asks whether the target matches, so a folder the toolkit dropped appears in none of them. `reverse` walks the target instead and reports a folder sitting at a top-level path the toolkit once shipped and has since deleted.
|
|
52
|
+
|
|
53
|
+
Each entry carries a verdict, since a dropped folder and one the project wrote are the same bytes at the same path.
|
|
54
|
+
|
|
55
|
+
- `dropped` names the commit that published the content
|
|
56
|
+
- `unattributed` means the toolkit shipped that path and the content matches no version it published
|
|
57
|
+
- `project` means the folder only shares a retired name
|
|
58
|
+
|
|
59
|
+
Nothing acts on any of them, and the verdict is what makes the list safe to read.
|
|
60
|
+
|
|
61
|
+
Add `--json` for the machine-readable report, and `--exit-code` to fail a CI job when a target falls behind. Files the project authored itself never count toward that exit code, and neither do superseded artifacts, seed drift, tooling, or anything the reverse walk reports, since each names content the project is expected to edit or place itself. An unmigrated domain does count, because running the relocation closes it.
|
|
62
|
+
|
|
63
|
+
Tooling reports under a section of its own, and `measured` there says whether the target ever recorded a chain. One that never ran a tooling sync reports unmeasured rather than clean, which is what separates tooling nobody has looked at from tooling that is current. A workspace root records nothing either way, since each package resolves its own chain.
|
|
64
|
+
|
|
65
|
+
Reconcile the configs with `canon tooling diff <stack> <path>` to read which files differ, then run `canon tooling sync <stack> <path> --write` to apply them. The drift report counts categories and the diff names paths, which is the difference worth knowing before a golden config the project edited is replaced.
|
|
66
|
+
|
|
67
|
+
## Catch-all
|
|
68
|
+
|
|
69
|
+
`canon sync <path>` runs every installed domain's sync in sequence. Safe to run on a cadence.
|
|
70
|
+
|
|
71
|
+
It never touches user-owned seed files. Governance rules in `.claude/rules/` and tooling configs refresh in place. Stale `.claude/GOV.md` from earlier installs is removed.
|
|
72
|
+
|
|
73
|
+
Standards take no part in that run. Nothing installed them, so there is no copy to reconcile and no `canon standards sync` to reach for.
|
|
74
|
+
|
|
75
|
+
## Targeted
|
|
76
|
+
|
|
77
|
+
- Claude seed docs such as `CLAUDE.md` and `canon/REQUIREMENTS.md`: invoke `canon:seed-sync`. The skill splits each file into a preamble (between the H1 and the first H2) plus one part per `##` section, then diffs part by part and proposes per-part edits. User customizations are preserved.
|
|
78
|
+
- Governance rules already installed: `canon gov sync <path>` diffs and applies, and never adds new rules. A rule your recorded stack lists reports as `missing` instead.
|
|
79
|
+
- Tooling configs and seeds: `canon tooling <stack> <path>` overwrites golden configs and merges seeds
|
|
80
|
+
- Reference docs for a stack: `canon tooling reference <stack>` reads and never writes, so there is nothing to sync
|
|
81
|
+
- Index regeneration after markdown edits: `canon indexes regen`
|
|
82
|
+
|
|
83
|
+
Use a targeted entry point when only one surface moved upstream. Use the catch-all when the toolkit lands a bundled release.
|
|
84
|
+
|
|
85
|
+
## Verify a sync
|
|
86
|
+
|
|
87
|
+
Before running a sync against a real project, run the relevant sandbox scenario. The sandbox provisions a representative project state and routes `SANDBOX_SCENARIO=sync` through the domain flow. See [sandbox](../canon/context/sandbox/index.md) for the scenario catalog and routing patterns.
|
|
88
|
+
|
|
89
|
+
## Running sync from an agent session
|
|
90
|
+
|
|
91
|
+
`canon sync .` applies every installed domain sync, then offers to commit the result and open a pull request. That last step needs a terminal. Under `CANON_NON_INTERACTIVE=1`, which is how an agent runs it, the domain syncs still apply and the git workflow is refused: the command reports the branch and commit it would have created, writes nothing to git, and exits 0. Review the working tree and commit it yourself, or rerun interactively to reach the commit and pull request options.
|
|
92
|
+
|
|
93
|
+
Sync also refuses a target whose working tree is dirty, so commit or stash before running it. `canon sync --check .` has neither restriction, since it writes nothing.
|