@erclx/canon 4.123.0 → 4.124.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/auto-ship/SKILL.md +2 -4
  3. package/claude/skills/codebase-layout/REQUIREMENT.md +1 -1
  4. package/claude/skills/codebase-layout/SKILL.md +1 -1
  5. package/claude/skills/create-rule/SKILL.md +5 -2
  6. package/claude/skills/design-taste/SKILL.md +1 -1
  7. package/claude/skills/draft-ready/REQUIREMENT.md +1 -1
  8. package/claude/skills/memory-review/SKILL.md +1 -1
  9. package/claude/skills/memory-review/references/later-phases.md +1 -1
  10. package/claude/skills/memory-review/references/receipt-format.md +1 -1
  11. package/claude/skills/test-first/REQUIREMENT.md +1 -1
  12. package/docs/agents/counts.md +3 -3
  13. package/docs/agents/drift-surfaces.md +2 -2
  14. package/docs/agents/rule-citations.md +4 -4
  15. package/docs/agents/test-order.md +1 -1
  16. package/docs/index.md +3 -0
  17. package/docs/target-migrations.md +93 -0
  18. package/docs/target-projects.md +4 -216
  19. package/docs/target-scenarios.md +56 -0
  20. package/docs/target-sync.md +93 -0
  21. package/docs/workflow/ai-workflow.md +2 -201
  22. package/docs/workflow/index.md +2 -0
  23. package/docs/workflow/operating-model.md +17 -46
  24. package/docs/workflow/parallel-features.md +77 -0
  25. package/docs/workflow/skill-map.md +148 -0
  26. package/governance/renames.toml +34 -0
  27. package/governance/rules/{claude/560-diagrams.md → canon/660-diagrams.md} +1 -1
  28. package/governance/rules/{claude/562-session.md → canon/662-session.md} +1 -1
  29. package/governance/rules/code/000-code.md +21 -0
  30. package/governance/rules/lib/300-testing-ts.md +0 -4
  31. package/governance/rules/lib/330-testing-py.md +0 -4
  32. package/governance/rules/{claude/500-prose.md → writing/800-prose.md} +1 -1
  33. package/governance/rules/{claude/501-markdown.md → writing/801-markdown.md} +1 -1
  34. package/governance/rules/{claude/502-mermaid.md → writing/802-mermaid.md} +1 -1
  35. package/governance/stacks/base.toml +7 -6
  36. package/governance/stacks/node.toml +1 -1
  37. package/governance/stacks/python.toml +1 -1
  38. package/package.json +1 -1
  39. package/scripts/core/install-check.sh +3 -5
  40. package/src/claude/routing.ts +1 -1
  41. package/src/commands/gov.ts +1 -1
  42. package/src/gate/measures.ts +1 -6
  43. package/src/gov/citations.ts +1 -1
  44. package/src/gov/list.ts +1 -1
  45. package/src/gov/restated.ts +2 -2
  46. package/src/init/steps.ts +1 -1
  47. package/src/labels/spelling.ts +1 -1
  48. package/src/records/prune.ts +1 -1
  49. package/src/shipped/references.ts +1 -1
  50. package/standards/rule.md +4 -6
  51. package/tooling/base/reference.md +1 -1
  52. package/tooling/claude/seeds/.claude/hooks/path-form.sh +1 -1
  53. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +1 -1
  54. package/tooling/web/reference.md +1 -1
  55. package/governance/rules/core/000-constitution.md +0 -30
  56. package/governance/rules/core/020-concurrency.md +0 -22
  57. package/governance/rules/core/030-error-handling.md +0 -31
  58. package/governance/rules/core/040-performance.md +0 -20
  59. package/governance/rules/core/050-logging.md +0 -20
  60. package/governance/rules/core/060-naming.md +0 -19
  61. package/governance/rules/snippets/600-at-references.md +0 -9
  62. /package/governance/rules/{core/025-indexes.md → canon/601-indexes.md} +0 -0
  63. /package/governance/rules/{core/035-tasks.md → canon/602-tasks.md} +0 -0
  64. /package/governance/rules/{core/045-memory.md → canon/603-memory.md} +0 -0
  65. /package/governance/rules/{core/055-scratch.md → canon/604-scratch.md} +0 -0
  66. /package/governance/rules/{core/085-worktrees.md → canon/605-worktrees.md} +0 -0
  67. /package/governance/rules/{core/087-git.md → canon/606-git.md} +0 -0
  68. /package/governance/rules/{claude/510-context.md → canon/610-context.md} +0 -0
  69. /package/governance/rules/{claude/511-indexes.md → canon/611-indexes.md} +0 -0
  70. /package/governance/rules/{claude/520-wireframes.md → canon/620-wireframes.md} +0 -0
  71. /package/governance/rules/{claude/530-requirements.md → canon/630-requirements.md} +0 -0
  72. /package/governance/rules/{claude/540-architecture.md → canon/640-architecture.md} +0 -0
  73. /package/governance/rules/{claude/545-decisions.md → canon/645-decisions.md} +0 -0
  74. /package/governance/rules/{claude/550-design.md → canon/650-design.md} +0 -0
  75. /package/governance/rules/{claude/555-tasks.md → canon/655-tasks.md} +0 -0
  76. /package/governance/rules/{claude/556-groundwork.md → canon/656-groundwork.md} +0 -0
  77. /package/governance/rules/{claude/557-intake.md → canon/657-intake.md} +0 -0
  78. /package/governance/rules/{claude/558-plan.md → canon/658-plan.md} +0 -0
  79. /package/governance/rules/{claude/559-memory.md → canon/659-memory.md} +0 -0
  80. /package/governance/rules/{claude/561-teach.md → canon/661-teach.md} +0 -0
  81. /package/governance/rules/{claude/563-ready.md → canon/663-ready.md} +0 -0
  82. /package/governance/rules/{core/005-behavior.md → claude/565-behavior.md} +0 -0
  83. /package/governance/rules/{core/015-output.md → claude/566-output.md} +0 -0
  84. /package/governance/rules/{core/070-planning.md → claude/567-planning.md} +0 -0
  85. /package/governance/rules/{core → code}/010-testing.md +0 -0
  86. /package/governance/rules/{ci → tooling}/700-ci-workflow.md +0 -0
  87. /package/governance/rules/{core/065-spelling.md → tooling/765-spelling.md} +0 -0
  88. /package/governance/rules/{core/075-dependencies.md → tooling/775-dependencies.md} +0 -0
  89. /package/governance/rules/{core/080-config-comments.md → tooling/780-config-comments.md} +0 -0
  90. /package/governance/rules/{core/090-code-comments.md → tooling/790-code-comments.md} +0 -0
  91. /package/governance/rules/{claude/580-readme.md → writing/880-readme.md} +0 -0
  92. /package/governance/rules/{claude/591-standard-authoring.md → writing/891-standard-authoring.md} +0 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "4.123.0",
4
+ "version": "4.124.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -99,8 +99,6 @@ If the diff touches UI files (JSX, TSX, Vue, Svelte, HTML, or CSS under `src/`),
99
99
 
100
100
  If `ui-checklist` produces a checklist, stop: `❌ UI requires visual verification. Checklist at .canon/tmp/handoff/ui-checklist/<slug>.md, which reaches the pull request once /git-ship runs. Verify manually, then run /git-ship.`
101
101
 
102
- The stop is the only thing that makes the operator look before `git-ship`.
103
-
104
102
  If there is nothing to verify visually and nothing shipping untested, continue.
105
103
 
106
104
  ## Step 6: review
@@ -137,7 +135,7 @@ Read origin as causation rather than authorship, so staleness this run induced i
137
135
 
138
136
  Bound the repair at one pass, the way Step 3 bounds verify. When that re-read shows the finding still standing, stop: `❌ A self-introduced finding survived one fix pass. See .canon/review/branch-<slug>.md. Fix and run /git-ship.`
139
137
 
140
- This chain owns the receipt's lifetime, which is what makes the Output block's citation resolve on a run that reaches it. The `context-fold` sweep under Step 8 reaches only reports whose branch is gone, which collects this one a branch later rather than during the run that wrote it.
138
+ This chain owns the receipt's lifetime. The `context-fold` sweep under Step 8 reaches only reports whose branch is gone, so it collects this one a branch later.
141
139
 
142
140
  ## Step 8: ship
143
141
 
@@ -145,7 +143,7 @@ Invoke `canon:git-ship`. That body owns the sequence, being the verify gate, mem
145
143
 
146
144
  One thing this chain adds. Mark the pull request as a draft as soon as `git-ship`'s pull request step returns, ahead of its CI watch, naming the number that step returned rather than one resolved by branch. Read `${CLAUDE_SKILL_DIR}/references/draft-mark.md` on reaching that point for the one command that proves the target, marks it, and reads the flag back. A `false` read stops the chain, and never re-issue the mark on it.
147
145
 
148
- `git-ship` verifies again at its own gate, which repeats this chain's Step 3 on the run where nothing stopped. That cost is deliberate, since most stop points hand the run straight back to that body, and a gate the chain skips for being redundant is a gate no resumed run ever meets.
146
+ `git-ship` verifies again at its own gate, repeating Step 3 on a clean run. Keep that repeat, since a resumed run enters at that gate.
149
147
 
150
148
  ## Output
151
149
 
@@ -30,7 +30,7 @@ Sharpening the rules does not reach the decision. Both were loaded and neither w
30
30
  - Carry a hard file-count threshold. A command folder holding sixty files of one role reads fine flat, and a number would fail it.
31
31
  - Restate the layer rule, the layer table, or the final filter `test-craft` owns
32
32
  - Mandate one layout across projects, such as a `features/` tree, over the project's own strategy
33
- - Restate the constitution's directory line. The rule points here and this body carries the depth.
33
+ - Restate the pointer in `000-code`, which loads in code stacks only. The rule points here and this body carries the depth.
34
34
 
35
35
  ## Guards
36
36
 
@@ -53,5 +53,5 @@ Read `${CLAUDE_SKILL_DIR}/references/adopted.md` only when extending this guidan
53
53
 
54
54
  - Which layer a test belongs at: `test-craft`. This skill decides where the test file sits once its layer is chosen.
55
55
  - Where a new path gets written into a plan, and the plan's shape: `plan-feature` and the plan standard
56
- - The directory principle every session loads: `000-constitution`
56
+ - The pointer here that every session of a code stack loads: `000-code`
57
57
  - Runner, suffix, and top-level test folder conventions per language: `300-testing-ts`, `330-testing-py`, `335-testing-go`, and `336-testing-php`
@@ -23,12 +23,15 @@ Resolve both from the request, and ask only for what is missing. Attach a propos
23
23
 
24
24
  Pick the subdir from the topic. It sits under `.claude/rules/project/` and names the rule's domain:
25
25
 
26
- - `core/`: global persona, testing, error handling, planning. Always-on, no `paths:`.
26
+ - `code/`: code placement, errors, naming, testing.
27
27
  - `lang/`: one programming language.
28
28
  - `framework/`: one framework.
29
29
  - `lib/`: one library or tool.
30
30
  - `ui/`: UI copy, accessibility, forms.
31
- - `claude/`: `.claude/` authoring surfaces.
31
+ - `claude/`: session conduct and `.claude/` authoring surfaces.
32
+ - `canon/`: the toolkit workflow and its record folders.
33
+ - `tooling/`: dev setup, such as CI workflows, dependencies, and config.
34
+ - `writing/`: prose and document shape.
32
35
 
33
36
  ## Step 3: pick a free number
34
37
 
@@ -44,7 +44,7 @@ Two worked examples:
44
44
 
45
45
  The read fixes the kind, which decides which rules apply, and names the layer, which decides what may vary. A round that cannot state its read has not decided what it is doing.
46
46
 
47
- Put the read to the operator through the structured question surface where it genuinely forks, per `005-behavior`. Where context settles it, declare and proceed rather than asking.
47
+ Put the read to the operator through the structured question surface where it genuinely forks, per `565-behavior`. Where context settles it, declare and proceed rather than asking.
48
48
 
49
49
  ## The layers, and the order they settle
50
50
 
@@ -50,4 +50,4 @@ Without this skill, a session holding finished files that hands them to a worker
50
50
  - `role-worker` copies the folder into a worktree and ships it. This stops once the three artifacts exist.
51
51
  - `task-board` creates a task file for a row with no ready folder. This writes the row it needs beside the folder and calls the same verbs.
52
52
  - `git-ship` ships a branch, and a ready folder never reaches one.
53
- - `standards/ready.md` owns the folder's shape and lifecycle, and this skill owns the procedure that produces it, the way `write-human` and `500-prose` split.
53
+ - `standards/ready.md` owns the folder's shape and lifecycle, and this skill owns the procedure that produces it, the way `write-human` and `800-prose` split.
@@ -72,7 +72,7 @@ An entry's `unresolved` list in the stale record names paths it cites that the t
72
72
  For each in-scope entry (see Scope), pick one action:
73
73
 
74
74
  - **Promote to an always-loaded rule**: the rule is cross-domain behavior or a design principle applied across the whole project, passing `592-claude-md.md`'s test (applies every session regardless of what is being edited). Do not author a toolkit rule file inline.
75
- - In the toolkit repo, hand off to `internal-governance` and `${CLAUDE_SKILL_DIR}/../../standards/rule.md`, which own `internal/rules/core/` (this repo only, never ships) and `governance/rules/core/` (ships to every target).
75
+ - In the toolkit repo, hand off to `internal-governance` and `${CLAUDE_SKILL_DIR}/../../standards/rule.md`, which own `internal/rules/` (this repo only, never ships) and the audience group under `governance/rules/` the rule serves (ships to every target that group reaches).
76
76
  - In a target project, append the rule to an existing file under `.claude/rules/project/` with `Edit`, since a project rule is project-owned and sync never overwrites it, or hand off to the `create-rule` skill when no existing file fits.
77
77
  - **Promote to a skill body**: the rule fires only when editing a specific path-scoped domain. Name the target skill.
78
78
  - **Promote to a standards file**: the rule is an authoring reference that belongs in the project's own standards folder as `<domain>.md`.
@@ -44,7 +44,7 @@ Before applying any item, check the worktree state:
44
44
 
45
45
  If the result is `MAIN`, stop and tell the user: `❌ Apply phase mutates tracked files. Run /session-worktree first.` Discuss and Challenge phases only touch `.canon/review/` scratch and run from anywhere.
46
46
 
47
- Before applying a promote to an always-loaded rule in the toolkit repo, load `internal-governance`, which owns `internal/rules/core/` and `governance/rules/core/` and is the only route that may author into either. In a target project, hand off to `create-rule` with no load, since `internal-governance` is a toolkit-internal skill under `.claude/skills/` that a target project never has. An `Edit` appending to an existing file under `.claude/rules/project/` needs no load either.
47
+ Before applying a promote to an always-loaded rule in the toolkit repo, load `internal-governance`, which owns `internal/rules/` and `governance/rules/` and is the only route that may author into either. In a target project, hand off to `create-rule` with no load, since `internal-governance` is a toolkit-internal skill under `.claude/skills/` that a target project never has. An `Edit` appending to an existing file under `.claude/rules/project/` needs no load either.
48
48
 
49
49
  Promotions are a separate concern from any feature in flight. Keep the promoted edits on their own commit. Do not fold an always-loaded-rule or skill-body change into a feature's commits, because a feature reviewer should not have to vet a change to how the agent operates.
50
50
 
@@ -61,4 +61,4 @@ Decision:
61
61
 
62
62
  ## Variation by action
63
63
 
64
- For Hand off items, the body is a pointer to the governance target instead of a rewritten rule: `internal-governance` and `${CLAUDE_SKILL_DIR}/../../standards/rule.md` in the toolkit repo, or the `create-rule` skill in a target project. For a Promote to an always-loaded rule item, the H2 target names the rule file path the rule lands in, `<target>` in the template above, being an existing path under `internal/rules/core/` or `governance/rules/core/` in the toolkit repo, or under `.claude/rules/project/` in a target project, or the `create-rule` pointer when no existing file fits. For Retire and Keep items, skip the rewrite block. A batch holding fewer than 25 entries says so in the `**Batch:**` line, and a scope the user named replaces that line with `**Batch:** named by the user, <n> entries`. Every item gets a `Decision:` slot regardless of action. `Take:` is added only when a question response is needed.
64
+ For Hand off items, the body is a pointer to the governance target instead of a rewritten rule: `internal-governance` and `${CLAUDE_SKILL_DIR}/../../standards/rule.md` in the toolkit repo, or the `create-rule` skill in a target project. For a Promote to an always-loaded rule item, the H2 target names the rule file path the rule lands in, `<target>` in the template above, being an existing path under `internal/rules/` or `governance/rules/` in the toolkit repo, or under `.claude/rules/project/` in a target project, or the `create-rule` pointer when no existing file fits. For Retire and Keep items, skip the rewrite block. A batch holding fewer than 25 entries says so in the `**Batch:**` line, and a scope the user named replaces that line with `**Batch:** named by the user, <n> entries`. Every item gets a `Decision:` slot regardless of action. `Take:` is added only when a question response is needed.
@@ -32,5 +32,5 @@ Without this skill, a session implementing a planned change writes the test afte
32
32
 
33
33
  - Choosing the layer a test belongs at and judging whether it is a good test: `canon:test-craft`, loaded at step 1 rather than restated here
34
34
  - Finding the cause of an unexplained failure: `canon:systematic-debugging`
35
- - Confirming visual output after a change lands: `070-planning.md` states the order and `ui-checklist` writes the list to confirm against
35
+ - Confirming visual output after a change lands: `567-planning.md` states the order and `ui-checklist` writes the list to confirm against
36
36
  - The mechanical audit of whether an implementation reached history ahead of its test: `canon gov test-order`, invoked from `auto-ship`
@@ -37,7 +37,7 @@ The set is closed rather than derived, so widening it is a deliberate change to
37
37
 
38
38
  A sentence has to carry an assertion verb (`loads`, `ships`, `carries`, `holds`, `counts`, `totals`, `documents`, `declares`, `installs`, `lists`, `contains`, `comprises`, `authors`), the quantifier `all`, or an article (`the`, `a`, `an`) immediately ahead of the number, which is immediately ahead of the catalog noun, with one optional qualifying word between the number and the noun (`sixty-one shipped skills`). The number reads either as digits or as a spelled-out cardinal through ninety-nine, since this corpus states a catalog size in words as often as in digits.
39
39
 
40
- Nothing stands between the trigger and the number, which is the rule an author writing a new count has to know. It separates `the toolkit authors 81 rules` and `took all 81 rules`, both read, from `a domain of 55 skills` and `denominator of sixty-one shipped skills`, neither of them read. A figure meant as a past state takes a date in the same sentence instead, which reads the sentence past whatever its shape.
40
+ Nothing stands between the trigger and the number, which is the rule an author writing a new count has to know. It separates `the toolkit authors 74 rules` and `took all 74 rules`, both read, from `a domain of 55 skills` and `denominator of sixty-one shipped skills`, neither of them read. A figure meant as a past state takes a date in the same sentence instead, which reads the sentence past whatever its shape.
41
41
 
42
42
  ## How the trigger set grew
43
43
 
@@ -45,7 +45,7 @@ The verb gate is not the first design tried. A bare number next to a catalog nou
45
45
 
46
46
  The article half was added after a first review of the shipped design found a live miss: the development domain's gating-stages entry stated a stale audit total while the tree held 20, and the verb gate never reached it, since that sentence puts its verb after the noun rather than ahead of the number. Widening the trigger set to admit an article ahead of the number closed that gap.
47
47
 
48
- The quantifier `all` and the verb `authors` joined on the same evidence, two more live misses failing only the trigger test. Allowing words to stand between the trigger and the number was the other candidate, measured and declined: at widths of one, two, and three it reached 77, 104, and 132 sentences against a baseline of 65, missed `took all 81 rules` at every width, and reached `the toolkit authors 81 rules` only at a width admitting the indirect-noun shape below. The two words reach both at 75 sentences and no false positive.
48
+ The quantifier `all` and the verb `authors` joined on the same evidence, two more live misses failing only the trigger test. Allowing words to stand between the trigger and the number was the other candidate, measured and declined: at widths of one, two, and three it reached 77, 104, and 132 sentences against a baseline of 65, missed `took all 74 rules` at every width, and reached `the toolkit authors 74 rules` only at a width admitting the indirect-noun shape below. The two words reach both at 75 sentences and no false positive.
49
49
 
50
50
  ## The plausibility filter
51
51
 
@@ -59,7 +59,7 @@ The article gate is looser than the verb list and carries a real cost. Re-runnin
59
59
 
60
60
  A delta phrased as a transition (`from fourteen to fifteen`), a fraction (`thirteen of sixteen`), and a total reached through an indirect noun (`denominator of sixty-one shipped skills`) are all catalog-size claims this corpus carries, and none matches the trigger shape this reads. Each stays a known gap. The false-positive rate that gated closing them has a measurement behind it now, and what it showed is that the widening these three need is the one that costs a false positive rather than the one that does not.
61
61
 
62
- A second figure in a sentence whose first figure already matched is a fourth gap and a structural one, since one match is taken per catalog per sentence. `authors 81 rules under governance/rules/ and consumes 62 into .claude/rules/` is read for its 81 alone, which is correct here because the two figures name different populations, and a sentence stating one catalog twice would go unread the same way.
62
+ A second figure in a sentence whose first figure already matched is a fourth gap and a structural one, since one match is taken per catalog per sentence. `authors 74 rules under governance/rules/ and consumes 62 into .claude/rules/` is read for its 74 alone, which is correct here because the two figures name different populations, and a sentence stating one catalog twice would go unread the same way.
63
63
 
64
64
  A calendar date (`2026-08-21`) or a backticked commit reference in the same sentence reads the whole sentence past, since that is how this corpus already marks a figure as a historical record rather than a live claim. `canon/ARCHITECTURE.md` and the context entries carry a figure this way deliberately, and every one of them stays correct forever.
65
65
 
@@ -114,8 +114,8 @@ that is not a full clone as unmeasured rather than clean. This gap does not
114
114
  reach the chain-based read above, since it consults no anchor at all.
115
115
 
116
116
  In the fallback, entitlement is filtered, because a stack does not receive
117
- every rule. The base stack takes the `core` and `claude` folders whole and
118
- every other stack extends it with individually named rules, so an unfiltered
117
+ every rule. The base stack takes four folders whole, the code stacks add `code`, and
118
+ every other stack extends them with individually named rules, so an unfiltered
119
119
  list would tell a base consumer about rules it can never receive and train the
120
120
  reader to skip the section.
121
121
 
@@ -24,7 +24,7 @@ Under `--json` the record holds stdout alone and the frame still renders on stde
24
24
 
25
25
  ## What the drift check cannot see
26
26
 
27
- `bun run check` asserts that an authored rule and its consumed copy agree, and a dead citation passes that assertion because both copies carry it. `governance/rules/claude/561-teach.md` told its reader to open a `references/glossary.md` inside the teach skill that had never existed at that path. The rule shipped, survived a release, and fired during a real teach run before anyone noticed.
27
+ `bun run check` asserts that an authored rule and its consumed copy agree, and a dead citation passes that assertion because both copies carry it. `governance/rules/canon/661-teach.md` told its reader to open a `references/glossary.md` inside the teach skill that had never existed at that path. The rule shipped, survived a release, and fired during a real teach run before anyone noticed.
28
28
 
29
29
  Two files agreeing is what a drift check measures. Whether either one is right about the tree is a different question, and this is the stage that asks it.
30
30
 
@@ -49,7 +49,7 @@ A `path` is anchored on the whole backticked span rather than on a trailing patt
49
49
  A rule body is full of backticked spans carrying a slash, and almost none of them names a file in this tree. The stage declines four shapes outright.
50
50
 
51
51
  - A placeholder or glob segment describes a shape rather than naming a file: `canon/context/<domain>.md`, `standards/<name>.md`, `app/**/route.ts`, `${CLAUDE_SKILL_DIR}/../../standards/<name>.md`.
52
- - A bare filename names a convention: `route.ts`, `manifest.toml`, `components.json`, `playwright.config.ts`. A bare name is read as a citation only when it matches a rule filename, which is how `562-session.md` points at `555-tasks.md`.
52
+ - A bare filename names a convention: `route.ts`, `manifest.toml`, `components.json`, `playwright.config.ts`. A bare name is read as a citation only when it matches a rule filename, which is how `662-session.md` points at `655-tasks.md`.
53
53
  - A span carrying no file extension is a folder or a module specifier: `src/pages/`, `next/font`, `try/except`, `react-hooks/set-state-in-effect`, `oven-sh/setup-bun@v2`, `@/lib/utils`.
54
54
  - A fenced block displays a path rather than pointing at one.
55
55
 
@@ -59,11 +59,11 @@ The extension test is what carries most of the separation, and it costs one real
59
59
 
60
60
  Two classes resolve to nothing and are right to. Both are reported by name rather than dropped, so a reader can see what the verdict declined to judge.
61
61
 
62
- **Governed.** A rule spelling a path in its own frontmatter `paths:` is naming an artifact a target holds rather than a file here. `governance/rules/claude/560-diagrams.md` declares `.claude/DIAGRAMS.md` and then tells its reader to convert one an older install left behind, so the file is correctly absent from this tree and correctly named in the rule.
62
+ **Governed.** A rule spelling a path in its own frontmatter `paths:` is naming an artifact a target holds rather than a file here. `governance/rules/canon/660-diagrams.md` declares `.claude/DIAGRAMS.md` and then tells its reader to convert one an older install left behind, so the file is correctly absent from this tree and correctly named in the rule.
63
63
 
64
64
  Only an exact declaration exempts, never a glob match against one. A glob declares a shape, so a body path sitting inside it is still a citation and a stale one is still a defect. A rule scoped at `docs/**` citing a `docs/agents/renamed.md` that moved is exactly the class this stage exists to catch, and matching the glob would excuse it.
65
65
 
66
- **Ignored.** A path git ignores is session scratch no clone is expected to hold. `governance/rules/claude/555-tasks.md` cites `.canon/tasks/index.md`, which is real in a live project and absent from a fresh clone and from every linked worktree. Resolving against the filesystem alone would make the verdict depend on which tree the stage ran in, so the unresolved paths go to one batched `git check-ignore` and an ignored one is excused. A read git cannot answer refuses rather than reporting those paths dead.
66
+ **Ignored.** A path git ignores is session scratch no clone is expected to hold. `governance/rules/canon/655-tasks.md` cites `.canon/tasks/index.md`, which is real in a live project and absent from a fresh clone and from every linked worktree. Resolving against the filesystem alone would make the verdict depend on which tree the stage ran in, so the unresolved paths go to one batched `git check-ignore` and an ignored one is excused. A read git cannot answer refuses rather than reporting those paths dead.
67
67
 
68
68
  ## Why the glob half reads one corpus
69
69
 
@@ -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/core/070-planning.md` that asks for the test first, which until this verb existed was prose loaded on a glob match with nothing measuring it.
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.
@@ -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 core rules, and scaffolds an empty `.claude/wiki/`. Most projects need nothing more.
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 projects pull changes per domain. There is one catch-all and several targeted entry points.
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
- ### Markdown-heavy project
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