@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.
- 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/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 +17 -46
- package/docs/workflow/parallel-features.md +77 -0
- package/docs/workflow/skill-map.md +148 -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/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/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
|
@@ -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
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
- `
|
|
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 `
|
|
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 `
|
|
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
|
|
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
|
|
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
|
|
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: `
|
|
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`
|
package/docs/agents/counts.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
118
|
-
every other stack extends
|
|
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/
|
|
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 `
|
|
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/
|
|
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/
|
|
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/
|
|
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
|