@erclx/canon 4.110.0 → 4.112.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/assets/brand/mark.svg +10 -0
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/auto-ship/REQUIREMENT.md +1 -0
- package/claude/skills/auto-ship/SKILL.md +12 -2
- package/claude/skills/canon-operator/REQUIREMENT.md +2 -2
- package/claude/skills/canon-operator/SKILL.md +3 -4
- package/claude/skills/git-pr/REQUIREMENT.md +2 -0
- package/claude/skills/git-pr/SKILL.md +21 -3
- package/claude/skills/target-check/references/domains.md +2 -2
- package/claude/skills/teach-workspace/SKILL.md +1 -1
- package/docs/agents/install-and-sync.md +4 -13
- package/docs/agents/teach.md +3 -1
- package/docs/target-projects.md +0 -2
- package/docs/workflow/ai-workflow.md +7 -11
- package/package.json +2 -1
- package/scripts/core/capture-home.sh +59 -0
- package/src/claude/cases/setup.ts +2 -23
- package/src/commands/sync.ts +0 -16
- package/src/design/components.ts +153 -152
- package/src/design/favicon.ts +8 -0
- package/src/design/tokens.ts +1 -1
- package/src/shipped/references.ts +1 -2
- package/src/sync/reverse.ts +3 -74
- package/src/teach/nav.ts +55 -13
- package/claude/skills/migration-claude-md/REQUIREMENT.md +0 -40
- package/claude/skills/migration-claude-md/SKILL.md +0 -79
- package/claude/skills/migration-context/REQUIREMENT.md +0 -36
- package/claude/skills/migration-context/SKILL.md +0 -97
- package/claude/skills/migration-standards-drop/REQUIREMENT.md +0 -55
- package/claude/skills/migration-standards-drop/SKILL.md +0 -113
- package/claude/skills/migration-superseded/REQUIREMENT.md +0 -44
- package/claude/skills/migration-superseded/SKILL.md +0 -115
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: migration-standards-drop
|
|
3
|
-
description: Proposes the ordered drop of an installed `.claude/standards/` tree and the repoint of every citation at `canon standards <name>`, separating a toolkit copy from a file the project wrote and naming any runtime reader before a delete is proposed. Use when asked to "drop the standards tree", "repoint the standards citations", "my project still has .claude/standards/", or when a toolkit update leaves an installed standards copy no command refreshes. Do NOT delete the tree, run the sync, or rewrite a citation. Proposal only.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Migration standards drop
|
|
7
|
-
|
|
8
|
-
## Guards
|
|
9
|
-
|
|
10
|
-
- If `.claude/` does not exist at `pwd`, stop: `❌ No .claude/ directory. Nothing here installed a standards tree.`
|
|
11
|
-
- If `.claude/standards/` holds no markdown, that is the pass: `✅ No installed standards tree. Nothing to drop.`
|
|
12
|
-
- If `canon` is not on `PATH`, stop: `❌ canon is not on PATH. Every citation this move writes names a verb the target has to run.`
|
|
13
|
-
|
|
14
|
-
Detection is the directory read above and nothing else. No report names this tree, since the sync engine registers governance alone and the unmigrated scan covers a root layout an older toolkit wrote rather than a copy under `.claude/`. A session waiting for a command to raise the tree waits forever.
|
|
15
|
-
|
|
16
|
-
## Step 1: read the version the sync would run from
|
|
17
|
-
|
|
18
|
-
Run `canon sync --check . --json` and read its `skew` object. The sync in Step 5 installs the rules the running binary carries, so a binary predating the citation change installs rules naming the path this move removes, and the sweep then has more to fix than it started with.
|
|
19
|
-
|
|
20
|
-
- `state` is `behind`: stop. `❌ The canon on PATH is <installed> against <latest> published. A gov sync from it installs rules citing the standards path. Update, then re-run.`
|
|
21
|
-
- `state` is `unknown`: report the reason and continue. A version that could not be read is unread rather than stale.
|
|
22
|
-
- `state` is `current`: continue.
|
|
23
|
-
|
|
24
|
-
## Step 2: split the tree three ways
|
|
25
|
-
|
|
26
|
-
Run `canon standards list --json`. Each entry carries `name` and `content`, so the comparison needs no second read. Match every `.claude/standards/<stem>.md` against `name`:
|
|
27
|
-
|
|
28
|
-
- **Matched, content identical.** A toolkit copy the corpus still carries unchanged. The drop loses nothing.
|
|
29
|
-
- **Matched, content differs.** A toolkit copy that has drifted, and the diff cannot say which side moved. Report it as a read the user owes rather than sorting it either way. All 20 files measured at one target on 2026-08-28 read as changed, because the corpus had moved on since the install, and none of them was project-authored.
|
|
30
|
-
- **Unmatched.** No standard resolves under that name. Either the project wrote the file or the toolkit retired the name, and the catalog cannot separate the two.
|
|
31
|
-
|
|
32
|
-
Report a project-authored file as one this move keeps. Its destination is `standards/<name>.md` at the project root, which `canon standards <name>` resolves ahead of the package corpus, so the citation form Step 4 gives every other file serves it too.
|
|
33
|
-
|
|
34
|
-
`prose` is the recorded retirement. It split into `markdown.md` plus the `write-human` skill and resolves as no standard at all, so a citation repointed one-to-one from it names nothing. Report an unmatched name as the user's to place and name that split as the instance.
|
|
35
|
-
|
|
36
|
-
## Step 3: name every runtime reader before naming a delete
|
|
37
|
-
|
|
38
|
-
Run both from the project root, in parallel:
|
|
39
|
-
|
|
40
|
-
- `git grep -ln 'claude/standards' -- '*.md'`: the citations Step 4 repoints
|
|
41
|
-
- `git grep -ln 'claude/standards' -- . ':!*.md'`: the readers
|
|
42
|
-
|
|
43
|
-
The extension is the split. Markdown cites the tree and a session reads the citation. A hook, a script, or a config opens a file under it at run time, and dropping the tree under one leaves a check whose input is gone reporting success. Two of the five targets in the 2026-08-28 census carried a reader, one of them a hook hand-parsing a standard for a banned-word set.
|
|
44
|
-
|
|
45
|
-
Report each reader with what replaces it. A reader parsing a standard for a list the CLI now ships reads that list through the verb instead, which is `canon markdown audit <file> --json` for the banned-word case.
|
|
46
|
-
|
|
47
|
-
When `pwd` is not a git work tree, say the reader search did not run rather than reporting no reader.
|
|
48
|
-
|
|
49
|
-
## Step 4: give every citation one target
|
|
50
|
-
|
|
51
|
-
Every citation repoints to `canon standards <name>`, whatever surface it sits on. One form rather than a choice per surface, which is the part a session gets wrong:
|
|
52
|
-
|
|
53
|
-
- A rule under `.claude/rules/` is markdown a glob match loads, so `${CLAUDE_SKILL_DIR}` expands to nothing there
|
|
54
|
-
- A project-local skill under `.claude/skills/<name>/` expands that variable to its own folder, so `${CLAUDE_SKILL_DIR}/../../standards/` resolves back into the tree being dropped
|
|
55
|
-
- `CLAUDE.md` and a context entry expand no variable at all
|
|
56
|
-
|
|
57
|
-
The plugin-root form belongs to a skill the toolkit ships beside its own corpus. Proposing it inside a target writes a path that resolves to the deleted tree.
|
|
58
|
-
|
|
59
|
-
A citation naming a name Step 2 left unmatched carries no target. Report it under what the user supplies.
|
|
60
|
-
|
|
61
|
-
## Step 5: output the ordered proposal
|
|
62
|
-
|
|
63
|
-
The order is fixed and the reverse is the failure. Deleting before the sync lands the installed rules citing a path that is gone, and sweeping before the sync repoints citations the sync then rewrites. The sweep re-reads after the sync for that reason, since the sync fixes the rules it installed and leaves a project's own rules and prose alone.
|
|
64
|
-
|
|
65
|
-
Take the drop command from whether the tree is tracked, read with `git ls-files .claude/standards`. A tracked tree goes out through `git rm -r` so the index moves with the files, and an untracked one through `rm -rf`. A tree holding both needs both commands, since `git rm -r` takes the tracked files, exits zero, and leaves every untracked one where it was.
|
|
66
|
-
|
|
67
|
-
Print the blocks below, omitting any that is empty.
|
|
68
|
-
|
|
69
|
-
````markdown
|
|
70
|
-
## Drop
|
|
71
|
-
|
|
72
|
-
`.claude/standards/` holds <n> files: <n> unchanged toolkit copies, <n> drifted, <n> matching no standard.
|
|
73
|
-
|
|
74
|
-
## Read before dropping
|
|
75
|
-
|
|
76
|
-
- `.claude/standards/<name>.md` drifted from the corpus copy. Confirm the project did not edit it.
|
|
77
|
-
|
|
78
|
-
## Keep
|
|
79
|
-
|
|
80
|
-
- `.claude/standards/<name>.md` matches no standard. Move it to `standards/<name>.md` at the project root, which `canon standards <name>` resolves first.
|
|
81
|
-
|
|
82
|
-
## Runtime readers
|
|
83
|
-
|
|
84
|
-
- `.claude/hooks/<file>.sh` parses a standard at run time. Route it through `canon markdown audit <file> --json` before the drop, or it reports a clean run having read nothing.
|
|
85
|
-
|
|
86
|
-
## Citations to repoint
|
|
87
|
-
|
|
88
|
-
- `CLAUDE.md:42` → `canon standards markdown`
|
|
89
|
-
- `.claude/rules/canon/core/<n>-<slug>.md:8` → `canon standards skill`
|
|
90
|
-
|
|
91
|
-
## You supply
|
|
92
|
-
|
|
93
|
-
- The destination for the `prose` citations. It split into `markdown.md` plus the `write-human` skill, so no single name replaces it.
|
|
94
|
-
|
|
95
|
-
## Run in this order
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
canon gov sync
|
|
99
|
-
git rm -r .claude/standards
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
Then re-read the citations and repoint what the sync left.
|
|
103
|
-
|
|
104
|
-
## Confirm
|
|
105
|
-
|
|
106
|
-
Run `canon standards <name>` from inside this project for every name the repointed citations carry, and confirm each resolves.
|
|
107
|
-
|
|
108
|
-
## Reminder
|
|
109
|
-
|
|
110
|
-
Nothing above was written or run. The tree holds files a delete cannot recover, so the drop and the sweep are yours to apply.
|
|
111
|
-
````
|
|
112
|
-
|
|
113
|
-
Run nothing and write nothing: not the sync, not a delete or a move under `.claude/standards/`, not a citation rewrite. The install stamp is out of scope too, and travels with whatever else reaches this target. The user applies the move after reviewing it.
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: migration-superseded
|
|
3
|
-
description: Why a retired file's split ships as a proposal read from the destination standard, and why detection stops rather than falling back to a listing
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Migration superseded requirement
|
|
7
|
-
|
|
8
|
-
## Gap
|
|
9
|
-
|
|
10
|
-
Without this skill, a target holding a retired `.claude/` file is told what replaced it and nothing else. The drift report names both halves and no command touches either, so the user hand-splits one file into a folder answering to a standard they have not read. The destination files land with frontmatter fields missing and filenames that sort out of order, which is the step the toolkit exists to remove.
|
|
11
|
-
|
|
12
|
-
A second failure comes from the ordering nobody gets right unaided. A retired file committed before its ignore entry was written is tracked and ignored at once, and removing the entry first leaves the file tracked with nothing naming it. No report catches that state, so the mistake is discovered by whoever next wonders why a gitignored path keeps appearing in diffs.
|
|
13
|
-
|
|
14
|
-
Two more follow from where a session looks when the report is thin. An uppercase stem under `.claude/` is not the test for a superseded file, so a session substituting a folder listing reaches `ARCHITECTURE.md`, `REQUIREMENTS.md`, and `DESIGN.md`, each a single file the layout intends to stay one, and proposes shredding three documents. A session falling back to the plugin's own copy of the destination standard proposes a shape the project never adopted, against content only the user can place.
|
|
15
|
-
|
|
16
|
-
The last is the two-speed release skew arriving as a confident wrong answer. `superseded` reached a release in `0.46.0`, and an older CLI exits zero with a well-formed report that never mentions the field. A session reading that absent key as an empty array reports a clean layout to exactly the population holding the retired files.
|
|
17
|
-
|
|
18
|
-
## Must
|
|
19
|
-
|
|
20
|
-
- Take detection from the report's `superseded` array alone, and stop when the key is absent rather than treating it as empty
|
|
21
|
-
- Resolve the governing standard by matching the replacement folder against the `appliesTo` the standards catalog declares, so a seed folder added later resolves without an edit to the body
|
|
22
|
-
- Read the destination shape with `canon standards <name>`, report the root it answered from, and propose nothing for a folder the catalog names no standard for
|
|
23
|
-
- Separate a folder no standard governs from one whose governing standard could not be read, since the catalog derives `appliesTo` from prose and answers an unparsed scope statement with an empty array
|
|
24
|
-
- Name the untrack command ahead of the ignore entry whenever a retired file is both tracked and ignored, and say when the check could not run
|
|
25
|
-
- Report what the destination standard leaves open, such as a phase label the retired file never recorded, rather than supplying a value for it
|
|
26
|
-
- Report the measured empty case as a pass, since the guard is what proves the section ran
|
|
27
|
-
|
|
28
|
-
## Must not
|
|
29
|
-
|
|
30
|
-
- Write, move, or delete the retired file or its replacement, or create the destination folder
|
|
31
|
-
- Run `git rm --cached` or edit the file carrying the ignore entry
|
|
32
|
-
- Substitute a folder listing for the `superseded` array
|
|
33
|
-
- Read the plugin's own copy of the destination standard when the target lacks that file
|
|
34
|
-
|
|
35
|
-
## Guards
|
|
36
|
-
|
|
37
|
-
- A directory with no `.claude/` stops, since nothing there was superseded and the report's sections would not be measured
|
|
38
|
-
|
|
39
|
-
## Out of scope
|
|
40
|
-
|
|
41
|
-
- Classifying `CLAUDE.md` sections into the three-tier model: `migration-claude-md`
|
|
42
|
-
- Relocating `docs/` files by audience: `migration-context`
|
|
43
|
-
- Reconciling a seed file against its source section by section, which diffs two files rather than splitting one into a folder: `seed-sync`
|
|
44
|
-
- Applying the split and running the untrack, which the user does after reviewing
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: migration-superseded
|
|
3
|
-
description: Proposes how to split a retired `.claude/` file into the folder that replaced it, reading the destination shape from the standard that folder answers to. Use when asked to "split TASKS.md", "my .claude/DIAGRAMS.md was replaced by a folder", "migrate the superseded file", or after a drift report names a superseded artifact. Do NOT write to the retired file or to its replacement, and do NOT run the untrack command. Proposal only.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Migration superseded
|
|
7
|
-
|
|
8
|
-
## Guards
|
|
9
|
-
|
|
10
|
-
- If `.claude/` does not exist at `pwd`, stop: `❌ No .claude/ directory. Nothing here was superseded.`
|
|
11
|
-
|
|
12
|
-
The guard is also what makes the report's `superseded` section measured. Every section is gated on the target being toolkit-managed, and a `.claude/` directory satisfies that gate, so the empty case below is a measured empty rather than a section that never ran.
|
|
13
|
-
|
|
14
|
-
## Step 1: read the report
|
|
15
|
-
|
|
16
|
-
Run `canon sync --check . --json` from the project root. Its `superseded` array is the detection. Each entry carries `rel`, the retired file, and `replacedBy`, the folder that took its job.
|
|
17
|
-
|
|
18
|
-
An empty array is the pass: `✅ No superseded artifact. Every .claude/ file the seeds replaced is already a folder.`
|
|
19
|
-
|
|
20
|
-
### When the report is unavailable
|
|
21
|
-
|
|
22
|
-
Stop on any of three conditions, naming which one fired:
|
|
23
|
-
|
|
24
|
-
- `canon` is not on `PATH`: `❌ canon is not on PATH. The superseded section is the only detection this skill has.`
|
|
25
|
-
- The command exits non-zero: `❌ canon sync --check failed. Fix the report before proposing a split.`
|
|
26
|
-
- The report parses and carries no `superseded` key at all: `❌ This canon predates the superseded field, which reached a release in 0.46.0. Upgrade, then re-run.`
|
|
27
|
-
|
|
28
|
-
Test for the key rather than for emptiness. A current CLI reporting `"superseded": []` has looked and found nothing, which is the pass above, and reading an absent key as an empty answer reports a clean layout to the projects this skill exists for.
|
|
29
|
-
|
|
30
|
-
No listing substitutes for the field. The pairing runs against the seed folder names the toolkit ships, and an uppercase stem is not the test, so a listing of `.claude/*.md` also reaches `ARCHITECTURE.md`, `REQUIREMENTS.md`, and `DESIGN.md`. Each of those is a single file the layout intends to stay one. A fallback that proposed splitting them would shred three documents to avoid reporting a stale CLI, which is why this skill stops where its siblings degrade.
|
|
31
|
-
|
|
32
|
-
## Step 2: resolve the destination standard
|
|
33
|
-
|
|
34
|
-
### Match the folder against the catalog
|
|
35
|
-
|
|
36
|
-
Run `canon standards list --json` and match each entry's `replacedBy` against the `appliesTo` array the catalog declares. Resolve from the catalog rather than from the folder stem, so a seed folder the toolkit adds later resolves without an edit here.
|
|
37
|
-
|
|
38
|
-
Compare on the folder rather than on the exact string. An `appliesTo` value matches when it equals `replacedBy` or begins with `replacedBy` followed by a slash. The report spells the folder `.canon/tasks` and the catalog spells it `.canon/tasks/`, and `memory` declares a filename pattern beneath its folder rather than the folder itself, so string equality matches nothing the catalog actually carries and sends every entry to a decline below.
|
|
39
|
-
|
|
40
|
-
### Read the standard through the verb
|
|
41
|
-
|
|
42
|
-
A match resolves to that standard's `name`. Read it with `canon standards <name>`, which writes the document to stdout and the root it answered from to stderr.
|
|
43
|
-
|
|
44
|
-
The verb is the one route. It resolves `standards/<name>.md` at the project root first, which is where a project that authors standards of its own keeps them, and falls back to the corpus inside the canon package. No toolkit standard installs into a project, so there is no third path to test and no case where the shape is unreachable while the catalog names it.
|
|
45
|
-
|
|
46
|
-
Report the root the frame named beside the proposal. A shape read from the project's own `standards/` is the project's stated agreement, and one read from the package is the toolkit's default, which is a difference the user weighs rather than a reason to refuse.
|
|
47
|
-
|
|
48
|
-
### The states that end an entry
|
|
49
|
-
|
|
50
|
-
Four states end an entry with no proposal. Name whichever one fired rather than collapsing them, because two of them are unmeasured and two are answers:
|
|
51
|
-
|
|
52
|
-
- The catalog carries no `appliesTo` key: `⚠️ This canon emits no appliesTo, so which standard governs <replacedBy> is unread.`
|
|
53
|
-
- No value covers `replacedBy` and some entry carries an empty `appliesTo`: an empty array is a scope statement that did not parse, so a no-match verdict is unread rather than negative. Name the standards that did not parse.
|
|
54
|
-
- No value covers `replacedBy` and every array is populated: `⚠️ The toolkit ships no standard for <replacedBy>. Nothing states the destination shape.` No command fixes it, so name none.
|
|
55
|
-
- The standard resolved in the catalog and `canon standards <name>` exits non-zero: name the standard and the exit, since the catalog and the read disagree and only one of them can be right.
|
|
56
|
-
|
|
57
|
-
## Step 3: read the shape and map the file onto it
|
|
58
|
-
|
|
59
|
-
Read the resolved standard and take four things from it: the filename convention, the required frontmatter, the required sections, and anything the folder holds that is not an item, such as a generated `index.md` no proposal may claim.
|
|
60
|
-
|
|
61
|
-
Then read the retired file and split it at the boundary the standard implies, one destination file per item. Propose a filename per item from the convention, and name the frontmatter and sections each destination file owes.
|
|
62
|
-
|
|
63
|
-
Report what the standard leaves open rather than inventing a value for it. A convention carrying a field the retired file never recorded, such as a phase label or a verified SHA, is the user's to supply, and a proposal that fills one in reads as a decision the split already made.
|
|
64
|
-
|
|
65
|
-
## Step 4: check the ignore ordering
|
|
66
|
-
|
|
67
|
-
Run both against each `rel`, in parallel:
|
|
68
|
-
|
|
69
|
-
- `git ls-files --error-unmatch <rel> 2>/dev/null`: whether the file is tracked
|
|
70
|
-
- `git check-ignore -v --no-index <rel> 2>/dev/null`: whether it is ignored, and which file and line names it
|
|
71
|
-
|
|
72
|
-
`--no-index` is what makes the second read answer the question. `git check-ignore` consults the index by default and reports a tracked path as not ignored, so the flagless form returns nothing in exactly the tracked-while-ignored state this step exists to find, and the run reads a clean result off the one case that is not clean.
|
|
73
|
-
|
|
74
|
-
A file that is both tracked and ignored carries an ignore entry added after the commit. Take it out of the index first, then remove the entry `check-ignore` named:
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
git rm --cached <rel>
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Reversing the two leaves the file tracked with nothing naming it, and no report catches that state. Name both steps in that order and run neither. `git rm --cached` writes the index, which is a change to the project's own history even though no content moves.
|
|
81
|
-
|
|
82
|
-
When `pwd` is not a git work tree, say the tracked check did not run rather than reporting the ordering as clear.
|
|
83
|
-
|
|
84
|
-
## Step 5: output
|
|
85
|
-
|
|
86
|
-
Print one block per superseded entry, then the shared reminder. Omit empty groups.
|
|
87
|
-
|
|
88
|
-
```markdown
|
|
89
|
-
## Split
|
|
90
|
-
|
|
91
|
-
`.claude/TASKS.md` → `.canon/tasks/`, shaped by `canon standards tasks`
|
|
92
|
-
|
|
93
|
-
## Proposed files
|
|
94
|
-
|
|
95
|
-
- `.canon/tasks/vXX.Y-<slug>.md` ← the "<heading>" section
|
|
96
|
-
- `.canon/tasks/vXX.Y-<slug>.md` ← the "<heading>" section
|
|
97
|
-
|
|
98
|
-
Each carries `title` and `description` frontmatter, an `## Outcomes` heading, and a `## Findings` heading.
|
|
99
|
-
|
|
100
|
-
## You supply
|
|
101
|
-
|
|
102
|
-
- The phase label on each filename. The retired file records no version.
|
|
103
|
-
|
|
104
|
-
## Untrack before editing the ignore entry
|
|
105
|
-
|
|
106
|
-
git rm --cached .claude/TASKS.md
|
|
107
|
-
|
|
108
|
-
Then remove the `.claude/TASKS.md` line from `.gitignore:12`.
|
|
109
|
-
|
|
110
|
-
## Reminder
|
|
111
|
-
|
|
112
|
-
Nothing above was written or run. The retired file holds content the project authored, so the split is yours to apply and the destination folder shape is what this proposal supplies.
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
Do not create the destination folder. Do not write, move, or delete the retired file. Do not run `git rm --cached`. The user applies the split after reviewing it.
|