@erclx/canon 4.6.1 → 4.7.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 (132) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/canon-feedback-file/SKILL.md +3 -3
  3. package/claude/skills/canon-feedback-triage/SKILL.md +1 -1
  4. package/claude/skills/canon-rollout/SKILL.md +1 -1
  5. package/claude/skills/canon-screencast/SKILL.md +5 -5
  6. package/claude/skills/canon-slides-draft/SKILL.md +1 -1
  7. package/claude/skills/claude-address-review/SKILL.md +4 -4
  8. package/claude/skills/claude-autoship/SKILL.md +17 -17
  9. package/claude/skills/claude-design-extract/SKILL.md +2 -2
  10. package/claude/skills/claude-diagram/SKILL.md +13 -13
  11. package/claude/skills/claude-docs/SKILL.md +23 -23
  12. package/claude/skills/claude-feature/SKILL.md +6 -6
  13. package/claude/skills/claude-groundwork/SKILL.md +16 -16
  14. package/claude/skills/claude-intake/SKILL.md +8 -8
  15. package/claude/skills/claude-intake-answer/SKILL.md +1 -1
  16. package/claude/skills/claude-markdown-propose/REQUIREMENT.md +2 -2
  17. package/claude/skills/claude-markdown-propose/SKILL.md +8 -8
  18. package/claude/skills/claude-markdown-propose/references/format.md +3 -3
  19. package/claude/skills/claude-memory-capture/SKILL.md +13 -13
  20. package/claude/skills/claude-memory-review/SKILL.md +28 -28
  21. package/claude/skills/claude-memory-review/references/receipt-format.md +1 -1
  22. package/claude/skills/claude-orchestrate/SKILL.md +9 -9
  23. package/claude/skills/claude-orchestrate/references/orchestrator-dispatch.md +1 -1
  24. package/claude/skills/claude-orchestrate/references/orchestrator-parked.md +1 -1
  25. package/claude/skills/claude-orchestrate/references/orchestrator-poll.md +1 -1
  26. package/claude/skills/claude-orchestrate/references/orchestrator-resume.md +1 -1
  27. package/claude/skills/claude-orchestrate/scripts/poll.sh +1 -1
  28. package/claude/skills/claude-planner/REQUIREMENT.md +1 -1
  29. package/claude/skills/claude-planner/SKILL.md +6 -6
  30. package/claude/skills/claude-pr-review/SKILL.md +5 -5
  31. package/claude/skills/claude-review/SKILL.md +4 -4
  32. package/claude/skills/claude-seed-sync/SKILL.md +2 -2
  33. package/claude/skills/claude-tasks/SKILL.md +16 -16
  34. package/claude/skills/claude-teach/SKILL.md +9 -9
  35. package/claude/skills/claude-ui-test/SKILL.md +3 -3
  36. package/claude/skills/claude-ux-audit/SKILL.md +5 -5
  37. package/claude/skills/claude-ux-measure/SKILL.md +3 -3
  38. package/claude/skills/claude-worker/SKILL.md +2 -2
  39. package/claude/skills/claude-worktree/SKILL.md +3 -3
  40. package/claude/skills/git-issue/SKILL.md +4 -4
  41. package/claude/skills/git-pr/SKILL.md +6 -6
  42. package/claude/skills/git-ship/SKILL.md +3 -3
  43. package/claude/skills/git-split/REQUIREMENT.md +1 -1
  44. package/claude/skills/git-split/SKILL.md +20 -20
  45. package/claude/skills/migration-superseded/SKILL.md +4 -4
  46. package/claude/skills/session-map/SKILL.md +2 -2
  47. package/claude/skills/session-resume/SKILL.md +8 -8
  48. package/docs/agents/capture.md +1 -1
  49. package/docs/agents/commands.md +5 -2
  50. package/docs/agents/demo.md +1 -1
  51. package/docs/agents/indexes.md +1 -1
  52. package/docs/agents/install-and-sync.md +1 -1
  53. package/docs/agents/intake.md +1 -1
  54. package/docs/agents/key-changes.md +1 -1
  55. package/docs/agents/records.md +12 -9
  56. package/docs/agents/restated.md +2 -2
  57. package/docs/agents/rule-citations.md +1 -1
  58. package/docs/agents/sandbox.md +1 -1
  59. package/docs/agents/tasks.md +6 -6
  60. package/docs/agents/teach.md +4 -4
  61. package/docs/ai-workflow.md +26 -19
  62. package/docs/operating-model.md +3 -3
  63. package/docs/target-projects.md +22 -2
  64. package/docs/visual-design-workflow.md +1 -1
  65. package/governance/rules/claude/555-tasks.md +3 -3
  66. package/governance/rules/claude/556-groundwork.md +2 -2
  67. package/governance/rules/claude/557-intake.md +2 -2
  68. package/governance/rules/claude/558-plan.md +3 -3
  69. package/governance/rules/claude/559-memory.md +2 -2
  70. package/governance/rules/claude/560-diagrams.md +3 -3
  71. package/governance/rules/claude/561-teach.md +2 -2
  72. package/governance/rules/claude/562-session.md +2 -2
  73. package/governance/rules/core/035-tasks.md +2 -2
  74. package/governance/rules/core/045-memory.md +3 -3
  75. package/governance/rules/core/055-scratch.md +2 -1
  76. package/governance/rules/core/085-worktrees.md +1 -1
  77. package/package.json +1 -1
  78. package/scripts/core/check-ignore-parity.sh +24 -51
  79. package/scripts/core/install-check.sh +4 -4
  80. package/scripts/core/snapshot.sh +1 -1
  81. package/scripts/manage-sandbox.sh +1 -1
  82. package/scripts/tooling/verify.sh +2 -2
  83. package/snippets/claude/decision-memo.md +1 -1
  84. package/snippets/claude/feature-recap.md +1 -1
  85. package/src/audits/catalog.ts +1 -1
  86. package/src/claude/cases/all.ts +1 -1
  87. package/src/claude/seeds.ts +13 -2
  88. package/src/claude/skills-rank.ts +1 -1
  89. package/src/cli.ts +4 -4
  90. package/src/commands/demo.ts +1 -1
  91. package/src/commands/design.ts +1 -1
  92. package/src/commands/feedback.ts +5 -5
  93. package/src/commands/gov.ts +17 -6
  94. package/src/commands/intake.ts +1 -1
  95. package/src/commands/migrate.ts +173 -2
  96. package/src/commands/pr.ts +1 -1
  97. package/src/commands/records.ts +1 -1
  98. package/src/commands/serve.ts +2 -2
  99. package/src/commands/slides.ts +1 -1
  100. package/src/commands/tasks.ts +5 -5
  101. package/src/commands/teach.ts +1 -1
  102. package/src/context/audit.ts +1 -1
  103. package/src/demo/compile.ts +1 -1
  104. package/src/gov/citations.ts +1 -1
  105. package/src/gov/restated.ts +3 -3
  106. package/src/migrate/apply.ts +67 -1
  107. package/src/migrate/records.ts +276 -0
  108. package/src/paths.ts +1 -1
  109. package/src/pr/paths.ts +1 -1
  110. package/src/record-root.ts +77 -27
  111. package/src/records/backup.ts +71 -17
  112. package/src/records/validate.ts +1 -1
  113. package/src/sync/layout.ts +11 -2
  114. package/src/tasks/answers.ts +1 -1
  115. package/src/tasks/archive.ts +20 -3
  116. package/standards/diagrams.md +4 -4
  117. package/standards/glossary.md +1 -1
  118. package/standards/groundwork.md +3 -3
  119. package/standards/index.md +2 -2
  120. package/standards/intake.md +3 -3
  121. package/standards/memory.md +2 -2
  122. package/standards/plan.md +3 -3
  123. package/standards/session.md +2 -2
  124. package/standards/standard.md +1 -1
  125. package/standards/tasks.md +9 -9
  126. package/standards/teach.md +2 -2
  127. package/standards/versioning.md +1 -1
  128. package/tooling/base/configs/.husky/post-merge +1 -1
  129. package/tooling/base/reference.md +1 -1
  130. package/tooling/claude/manifest.toml +13 -3
  131. package/tooling/claude/reference.md +28 -15
  132. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +8 -8
@@ -23,7 +23,7 @@ Run `git diff --cached --name-only 2>/dev/null` to check for staged files. If ou
23
23
 
24
24
  ## Sequence
25
25
 
26
- 1. Invoke `canon:claude-memory-capture` to route what this session learned to the context entries that own it and write the residue to `.claude/memory/`
26
+ 1. Invoke `canon:claude-memory-capture` to route what this session learned to the context entries that own it and write the residue to `.canon/memory/`
27
27
  2. Invoke `canon:claude-docs` to sync internal planning docs against session decisions, folding in the routed facts
28
28
  3. Invoke `canon:docs-sync` to sync public docs against changes since main
29
29
  4. Run `git add -A` to stage any files the sync skills wrote
@@ -46,8 +46,8 @@ Output up to four lines:
46
46
  ```plaintext
47
47
  ✅ Shipped
48
48
  <N facts routed to context entries>
49
- <N memories captured in .claude/memory/>
50
- <Memory proposal at .claude/review/memory/memory-review-<slug>.md>
49
+ <N memories captured in .canon/memory/>
50
+ <Memory proposal at .canon/review/memory/memory-review-<slug>.md>
51
51
  ```
52
52
 
53
53
  Omit the second line if nothing routed. Omit the third and fourth if `claude-memory-capture` wrote no memory file this session, since an empty pen means no scoped review and no proposal.
@@ -7,7 +7,7 @@ description: What branch splitting is for, the gaps it closes, and how it differ
7
7
 
8
8
  ## Gap
9
9
 
10
- Without this skill, a branch carrying unrelated commits ships as one pull request, which a reviewer reads by skimming because no single concern holds it together. Split by hand, commits get cherry-picked in an order that leaves a branch missing something it depends on. Merge order is the sharpest failure: merging the wrong branch first lands every other branch's content on main and leaves the remaining pull requests empty after rebase, with no signal that anything went wrong. Every body it writes stages under `.claude/.tmp/` and leaves through `gh`, neither of which the audit hook watches, so a banned character survives into a published pull request and has to be edited on the remote.
10
+ Without this skill, a branch carrying unrelated commits ships as one pull request, which a reviewer reads by skimming because no single concern holds it together. Split by hand, commits get cherry-picked in an order that leaves a branch missing something it depends on. Merge order is the sharpest failure: merging the wrong branch first lands every other branch's content on main and leaves the remaining pull requests empty after rebase, with no signal that anything went wrong. Every body it writes stages under `.canon/tmp/` and leaves through `gh`, neither of which the audit hook watches, so a banned character survives into a published pull request and has to be edited on the remote.
11
11
 
12
12
  ## Must
13
13
 
@@ -69,7 +69,7 @@ If Mode is Independent, append this line to the preview:
69
69
 
70
70
  After outputting the preview, execute the final commands immediately. Claude Code's tool permission dialog is the confirmation gate. Do not wait for user input.
71
71
 
72
- Before running them, run the scan in `${CLAUDE_SKILL_DIR}/../../standards/publish.md` against every title and body. Each body stages under `.claude/.tmp/` and leaves through `gh`, so the hook sees neither and this scan is the only gate. Each resulting branch reaches the `pull_request` check the git-pr surface carries once its own pull request opens, but not before, so this scan is what catches a leak ahead of that.
72
+ Before running them, run the scan in `${CLAUDE_SKILL_DIR}/../../standards/publish.md` against every title and body. Each body stages under `.canon/tmp/` and leaves through `gh`, so the hook sees neither and this scan is the only gate. Each resulting branch reaches the `pull_request` check the git-pr surface carries once its own pull request opens, but not before, so this scan is what catches a leak ahead of that.
73
73
 
74
74
  ### Final commands
75
75
 
@@ -80,25 +80,25 @@ For independent mode, base every branch on `main`:
80
80
  git branch -m <current_branch> <new_name>
81
81
 
82
82
  # Create, cherry-pick, push, and open PR for each secondary branch
83
- mkdir -p .claude/.tmp/pr-split
83
+ mkdir -p .canon/tmp/pr-split
84
84
  git switch -c <branch> main && git cherry-pick <sha> <sha> \
85
85
  && git push -u origin <branch> \
86
- && (cat <<'BODY' > .claude/.tmp/pr-split/<branch>.md
86
+ && (cat <<'BODY' > .canon/tmp/pr-split/<branch>.md
87
87
  <body following pr.md template, written from the cherry-picked commits>
88
88
  BODY
89
- ) && gh pr create --title "<title>" --body-file .claude/.tmp/pr-split/<branch>.md \
90
- && rm .claude/.tmp/pr-split/<branch>.md
89
+ ) && gh pr create --title "<title>" --body-file .canon/tmp/pr-split/<branch>.md \
90
+ && rm .canon/tmp/pr-split/<branch>.md
91
91
 
92
92
  # Return to primary branch, push, and open its PR
93
93
  git checkout <new_name> && git push -u origin <new_name> \
94
- && (cat <<'BODY' > .claude/.tmp/pr-split/<new_name>.md
94
+ && (cat <<'BODY' > .canon/tmp/pr-split/<new_name>.md
95
95
  <body following pr.md template, written from the primary's commits>
96
96
  BODY
97
- ) && gh pr create --title "<title>" --body-file .claude/.tmp/pr-split/<new_name>.md \
98
- && rm .claude/.tmp/pr-split/<new_name>.md
97
+ ) && gh pr create --title "<title>" --body-file .canon/tmp/pr-split/<new_name>.md \
98
+ && rm .canon/tmp/pr-split/<new_name>.md
99
99
 
100
100
  # Clean up the body-file dir if all PRs succeeded (no-op when non-empty)
101
- rmdir .claude/.tmp/pr-split 2>/dev/null || true
101
+ rmdir .canon/tmp/pr-split 2>/dev/null || true
102
102
  ```
103
103
 
104
104
  For stacked mode, base each branch on the previous and cherry-pick only that group's commits:
@@ -107,36 +107,36 @@ For stacked mode, base each branch on the previous and cherry-pick only that gro
107
107
  # Rename current branch to reflect primary concern
108
108
  git branch -m <current_branch> <new_name>
109
109
 
110
- mkdir -p .claude/.tmp/pr-split
110
+ mkdir -p .canon/tmp/pr-split
111
111
 
112
112
  # Group 1: based on main
113
113
  git switch -c <branch-1> main && git cherry-pick <g1-sha> <g1-sha> \
114
114
  && git push -u origin <branch-1> \
115
- && (cat <<'BODY' > .claude/.tmp/pr-split/<branch-1>.md
115
+ && (cat <<'BODY' > .canon/tmp/pr-split/<branch-1>.md
116
116
  <body following pr.md template, written from the cherry-picked commits>
117
117
  BODY
118
- ) && gh pr create --title "<title>" --body-file .claude/.tmp/pr-split/<branch-1>.md \
119
- && rm .claude/.tmp/pr-split/<branch-1>.md
118
+ ) && gh pr create --title "<title>" --body-file .canon/tmp/pr-split/<branch-1>.md \
119
+ && rm .canon/tmp/pr-split/<branch-1>.md
120
120
 
121
121
  # Group 2: based on <branch-1>, this group's commits only
122
122
  git checkout -b <branch-2> && git cherry-pick <g2-sha> <g2-sha> \
123
123
  && git push -u origin <branch-2> \
124
- && (cat <<'BODY' > .claude/.tmp/pr-split/<branch-2>.md
124
+ && (cat <<'BODY' > .canon/tmp/pr-split/<branch-2>.md
125
125
  <body following pr.md template, written from the cherry-picked commits>
126
126
  BODY
127
- ) && gh pr create --title "<title>" --body-file .claude/.tmp/pr-split/<branch-2>.md \
128
- && rm .claude/.tmp/pr-split/<branch-2>.md
127
+ ) && gh pr create --title "<title>" --body-file .canon/tmp/pr-split/<branch-2>.md \
128
+ && rm .canon/tmp/pr-split/<branch-2>.md
129
129
 
130
130
  # Return to primary branch, push, and open its PR
131
131
  git checkout <new_name> && git push -u origin <new_name> \
132
- && (cat <<'BODY' > .claude/.tmp/pr-split/<new_name>.md
132
+ && (cat <<'BODY' > .canon/tmp/pr-split/<new_name>.md
133
133
  <body following pr.md template, written from the primary's commits>
134
134
  BODY
135
- ) && gh pr create --title "<title>" --body-file .claude/.tmp/pr-split/<new_name>.md \
136
- && rm .claude/.tmp/pr-split/<new_name>.md
135
+ ) && gh pr create --title "<title>" --body-file .canon/tmp/pr-split/<new_name>.md \
136
+ && rm .canon/tmp/pr-split/<new_name>.md
137
137
 
138
138
  # Clean up the body-file dir if all PRs succeeded (no-op when non-empty)
139
- rmdir .claude/.tmp/pr-split 2>/dev/null || true
139
+ rmdir .canon/tmp/pr-split 2>/dev/null || true
140
140
  ```
141
141
 
142
142
  ## After execution
@@ -35,7 +35,7 @@ No listing substitutes for the field. The pairing runs against the seed folder n
35
35
 
36
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
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 `.claude/tasks` and the catalog spells it `.claude/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.
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
39
 
40
40
  ### Read the standard through the verb
41
41
 
@@ -88,12 +88,12 @@ Print one block per superseded entry, then the shared reminder. Omit empty group
88
88
  ```markdown
89
89
  ## Split
90
90
 
91
- `.claude/TASKS.md` → `.claude/tasks/`, shaped by `canon standards tasks`
91
+ `.claude/TASKS.md` → `.canon/tasks/`, shaped by `canon standards tasks`
92
92
 
93
93
  ## Proposed files
94
94
 
95
- - `.claude/tasks/vXX.Y-<slug>.md` ← the "<heading>" section
96
- - `.claude/tasks/vXX.Y-<slug>.md` ← the "<heading>" section
95
+ - `.canon/tasks/vXX.Y-<slug>.md` ← the "<heading>" section
96
+ - `.canon/tasks/vXX.Y-<slug>.md` ← the "<heading>" section
97
97
 
98
98
  Each carries `title` and `description` frontmatter, an `## Outcomes` heading, and a `## Findings` heading.
99
99
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: session-map
3
- description: Writes the session map, the pre-compaction handoff at `.claude/tasks/session-<slug>.md`, from any session whatever role it holds, running the skill-drift step the write procedure opens with. Use when asked to "write the handoff", "write the session map", "save the session before it compacts", "we are about to compact", "hand off to the next session", or "leave a note for whoever picks this up". Do NOT use to route session facts to a context entry or the memory folder, which is `claude-memory-capture` and writes a different artifact, and do NOT use to read a handoff back, which is `session-resume`.
3
+ description: Writes the session map, the pre-compaction handoff at `.canon/tasks/session-<slug>.md`, from any session whatever role it holds, running the skill-drift step the write procedure opens with. Use when asked to "write the handoff", "write the session map", "save the session before it compacts", "we are about to compact", "hand off to the next session", or "leave a note for whoever picks this up". Do NOT use to route session facts to a context entry or the memory folder, which is `claude-memory-capture` and writes a different artifact, and do NOT use to read a handoff back, which is `session-resume`.
4
4
  ---
5
5
 
6
6
  # Session map
@@ -56,7 +56,7 @@ The file sits at the main worktree root, which the file-editing tools refuse fro
56
56
  ## Output
57
57
 
58
58
  ```plaintext
59
- ✅ Session map written: .claude/tasks/session-<slug>.md
59
+ ✅ Session map written: .canon/tasks/session-<slug>.md
60
60
  <what the drift verb named, or the boundary it refused at>
61
61
  ```
62
62
 
@@ -7,14 +7,14 @@ description: Resumes a previous session by reading the handoff it left behind, t
7
7
 
8
8
  ## Step 1: read tracked work
9
9
 
10
- Resolve `.claude/plans/`, `.claude/memory/`, and `.claude/tasks/` at the main worktree root per Worktrees in `CLAUDE.md`.
10
+ Resolve `.canon/plans/`, `.canon/memory/`, and `.canon/tasks/` at the main worktree root per Worktrees in `CLAUDE.md`.
11
11
 
12
12
  Read these in parallel, skipping any that do not exist:
13
13
 
14
- - the newest `.claude/tasks/session-*.md`: the handoff a previous session wrote before a compaction, per `${CLAUDE_SKILL_DIR}/../../standards/session.md`. It leads the report rather than the reads.
15
- - `.claude/tasks/index.md`: the folder catalog. Read this before any individual task file, and take the task list from it by dropping the `index`, `priority`, `backlog`, and `session-` rows, which are siblings rather than tasks.
16
- - `.claude/plans/*.md`: execution detail for in-progress tasks
17
- - `.claude/memory/index.md` and any memory files relevant to the top backlog item
14
+ - the newest `.canon/tasks/session-*.md`: the handoff a previous session wrote before a compaction, per `${CLAUDE_SKILL_DIR}/../../standards/session.md`. It leads the report rather than the reads.
15
+ - `.canon/tasks/index.md`: the folder catalog. Read this before any individual task file, and take the task list from it by dropping the `index`, `priority`, `backlog`, and `session-` rows, which are siblings rather than tasks.
16
+ - `.canon/plans/*.md`: execution detail for in-progress tasks
17
+ - `.canon/memory/index.md` and any memory files relevant to the top backlog item
18
18
 
19
19
  Then read only the task files the summary needs, typically the top one. Do not read the whole folder.
20
20
 
@@ -28,9 +28,9 @@ Output these sections, omitting the first when no handoff was found:
28
28
 
29
29
  **Carried over:** the reasoning the handoff holds, as its writer stated it. Attribute it to the map rather than restating it as fact, and re-measure any count, size, or cost before acting on one.
30
30
 
31
- **Up next:** one line per task row in `.claude/tasks/index.md`, preserving order. A board carrying handoffs has a row per session that wrote one, so a report listing every row queues work nobody filed.
31
+ **Up next:** one line per task row in `.canon/tasks/index.md`, preserving order. A board carrying handoffs has a row per session that wrote one, so a report listing every row queues work nobody filed.
32
32
 
33
- **Active plans:** one line per file in `.claude/plans/`, linking each to its task file in `.claude/tasks/`. Say "None" if empty.
33
+ **Active plans:** one line per file in `.canon/plans/`, linking each to its task file in `.canon/tasks/`. Say "None" if empty.
34
34
 
35
35
  **Relevant context:** two or three memory entries that inform the top backlog item. Skip if none apply.
36
36
 
@@ -40,7 +40,7 @@ End with one line: `Start with: <first Up next item>` and note whether it has a
40
40
 
41
41
  When the board is empty and a handoff was found, name what the handoff leaves open instead: `Start with: <the open thread the handoff names>`. A recommendation slot filled with nothing reads as a failed run, and a handoff on an empty board is the shape a session leaves when it was reasoning rather than shipping.
42
42
 
43
- Do not offer to remove entries. A completed task is archived out of `.claude/tasks/` when work ships. The git log is the authoritative record of shipped work. Plan files are archived per the plan lifecycle rule in `CLAUDE.md`.
43
+ Do not offer to remove entries. A completed task is archived out of `.canon/tasks/` when work ships. The git log is the authoritative record of shipped work. Plan files are archived per the plan lifecycle rule in `CLAUDE.md`.
44
44
 
45
45
  Memory is updated only when a recorded fact becomes wrong, never on resume. A domain fact reaches a session through `.claude/context/`, which `claude-memory-capture` routes to and the three-tier model loads on demand, so the memory folder read here is the residue no context entry owns.
46
46
 
@@ -10,7 +10,7 @@ description: Rendering committed HTML sources to PNG, what the command asserts a
10
10
  ```bash
11
11
  canon capture
12
12
  canon capture assets/install.html
13
- canon capture assets --out .claude/review/captures
13
+ canon capture assets --out .canon/review/captures
14
14
  ```
15
15
 
16
16
  `assets/` now holds two sources, so a bare `canon capture` rebuilds both. `install.html` is hand-authored and its text came from a real run. `hero.html` is generated by `scripts/core/regen-hero.sh` from the CLI catalogs and must not be edited by hand, since `bun run check` regenerates it and fails on the difference.
@@ -24,7 +24,7 @@ Full help: `canon <command> --help`. Behavior notes for the install and sync ver
24
24
  | `canon design render` | Render `.claude/DESIGN.md` tokens to HTML and CSS |
25
25
  | `canon slides render` | Render a `.claude/SLIDES.md` source into a PowerPoint deck, reporting any unrecognized layout name on stderr |
26
26
  | `canon slides list` | List the available slide layouts (`--json` for the catalog) |
27
- | `canon feedback` | Write toolkit feedback from stdin to `.claude/review/feedback/`, or open a GitHub issue with `--github` |
27
+ | `canon feedback` | Write toolkit feedback from stdin to `.canon/review/feedback/`, or open a GitHub issue with `--github` |
28
28
  | `canon transcripts <url>` | Fetch a YouTube transcript with metadata frontmatter (needs `yt-dlp`) |
29
29
  | `canon tasks archive` | Move a shipped task off the board, clear its ordering row, and regenerate the index |
30
30
  | `canon tasks pull-request` | Record a pull request number on the task a branch closes, by stem or `--plan` (`--json`) |
@@ -42,6 +42,7 @@ Full help: `canon <command> --help`. Behavior notes for the install and sync ver
42
42
  | `canon records push` | Commit the nine backed record folders and push them to a private records remote (`--json`) |
43
43
  | `canon records pull` | Fetch the records remote and write it back, refusing rather than discarding unpushed records (`--json`) |
44
44
  | `canon migrate rename` | Rewrite every unprotected `aitk` token to `canon` and move the paths that carry the name, reporting the plan without `--write` (`--scope`, `--json`) |
45
+ | `canon migrate records` | Move the gitignored session records to `.canon/` and repoint every tracked citation, reporting the plan without `--write` (`--root`, `--json`) |
45
46
  | `canon sessions list` | Resolve live sessions to the worktree and branch each holds, filtered by `--branch` (`--json`) |
46
47
  | `canon worktrees list` | Report which worktrees are reclaimable, keyed on the pull request having merged, with every refusal and the removal route named (`--json`) |
47
48
  | `canon comments scan` | Measure comment density by language and comment kind, with a trend recomputed from git |
@@ -122,7 +123,7 @@ Each domain exposes a consistent shape where applicable: `list`, `install`, `syn
122
123
  | `secrets` | `scan` |
123
124
  | `deps` | `audit` |
124
125
  | `labels` | `audit` |
125
- | `migrate` | `rename` |
126
+ | `migrate` | `rename`, `records` |
126
127
  | `autoship` | `classify` |
127
128
  | `audits` | `run`, `list` |
128
129
  | `gate` | `run` |
@@ -136,6 +137,8 @@ Common patterns:
136
137
 
137
138
  `migrate rename` moves a project off the retired `aitk` name. It reports until `--write` is passed, and `--scope target` rewrites the toolkit-owned folders alone, reporting every other citation as one the project owns rather than editing prose somebody wrote. A project installing `canon` fresh never needs it.
138
139
 
140
+ `migrate records` moves a project's session records from `.claude/` to `.canon/` and rewrites every tracked file that cites one. It reports until `--write` is passed, and refuses outright when the project does not already ignore `.canon/`, since every folder it relocates is ignored where it stands and landing one under a tracked root commits the memory pen. Take the ignore entry with `canon tooling sync --write` first. A record folder already present at the destination is a refusal rather than a merge, and a line carrying `canon-keep-record-root`, or the line below it, keeps the old spelling for prose that dates a decision. Running it twice rewrites nothing, which is the check that the exclusions and the markers fired.
141
+
139
142
  ## Version skew
140
143
 
141
144
  `canon sync --check` and `canon claude skills drift` are the two moments a target
@@ -10,7 +10,7 @@ description: Compiling a screencast draft into a runnable plan, driving a served
10
10
  Two verbs, and they are separate because the artifact between them is edited.
11
11
 
12
12
  ```bash
13
- canon demo compile .claude/.tmp/screencast/inline-edit.md
13
+ canon demo compile .canon/tmp/screencast/inline-edit.md
14
14
  canon demo run demos/inline-edit.json
15
15
  canon demo run demos/inline-edit.json --cursor ~/cursors/theme --out assets
16
16
  ```
@@ -9,7 +9,7 @@ description: Flags, exit codes, and JSON shape for canon indexes regen, plus whe
9
9
 
10
10
  Duplicates dedupe. The whole-repo walk prunes `.git`, `node_modules`, and anything `.gitignore` covers via `git check-ignore`.
11
11
 
12
- A positional path is not filtered that way, because the walk-up resolves on the filesystem and never consults git. That is the only way to regenerate an index inside a gitignored folder, and it is how `.claude/tasks/` and `.claude/memory/` both stay current.
12
+ A positional path is not filtered that way, because the walk-up resolves on the filesystem and never consults git. That is the only way to regenerate an index inside a gitignored folder, and it is how `.canon/tasks/` and `.canon/memory/` both stay current.
13
13
 
14
14
  Each is driven by a `PostToolUse` hook matching `Write|Edit|MultiEdit`, so a file moved by a shell `mv` fires nothing. A caller that relocates an entry runs the command itself once the last move is done.
15
15
 
@@ -205,7 +205,7 @@ target never touched still reports `matching`. Every other seed copies byte for
205
205
  byte.
206
206
 
207
207
  `superseded` names a file a newer seed folder replaced, such as `.claude/TASKS.md`
208
- against the `.claude/tasks/` that now ships. The entry carries `replacedBy` and
208
+ against the `.canon/tasks/` that now ships. The entry carries `replacedBy` and
209
209
  nothing else, and the file is never deleted, since the content belongs to the
210
210
  project and only its author can decide where it moves. The list derives from the
211
211
  seed tree rather than from a fixed set of filenames, so a folder added later is
@@ -7,7 +7,7 @@ description: Reading intake folder counts and items, the three read states an it
7
7
 
8
8
  ## List
9
9
 
10
- `canon intake list` reports the intake folders under `.claude/intake/`, or the items one folder holds. It reads and never writes, because an answer belongs to the operator and a verb that filled one would decide what the folder exists to ask.
10
+ `canon intake list` reports the intake folders under `.canon/intake/`, or the items one folder holds. It reads and never writes, because an answer belongs to the operator and a verb that filled one would decide what the folder exists to ask.
11
11
 
12
12
  A folder carries a two-digit ordinal ahead of its slug, as in `21-toolkit-overview`, so a listing sorts by when each opened. A bare slug still resolves: passing `toolkit-overview` matches the one folder whose name is an ordinal ahead of it, and the folder's real name is what every command reports back.
13
13
 
@@ -10,7 +10,7 @@ description: Comparing the files a pull request body's Key Changes names against
10
10
  ```bash
11
11
  canon pr key-changes
12
12
  canon pr key-changes 1265 --json
13
- canon pr key-changes --body .claude/.tmp/pr/body.md --base origin/main
13
+ canon pr key-changes --body .canon/tmp/pr/body.md --base origin/main
14
14
  ```
15
15
 
16
16
  The positional is the pull request to read, defaulting to the one open on this branch.
@@ -7,11 +7,11 @@ description: The two roots a record folder resolves at, validating the session r
7
7
 
8
8
  ## Record roots
9
9
 
10
- Every verb here resolves a record folder at two roots rather than one. `.canon/<folder>` is read first, `.claude/<folder>` second, and a folder neither root carries resolves to the creation default, which is `.claude/`. The scratch folder is the one name that differs by root, spelled `.claude/.tmp` and `.canon/tmp`, since inside a dotted root the leading dot hides nothing already hidden.
10
+ Every verb here resolves a record folder at two roots rather than one. `.canon/<folder>` is read first, `.claude/<folder>` second, and a folder neither root carries resolves to the creation default, which is `.claude/`. The scratch folder is the one name that differs by root, spelled `.canon/tmp` and `.canon/tmp`, since inside a dotted root the leading dot hides nothing already hidden.
11
11
 
12
12
  The read order and the creation default disagree deliberately. The gitignored record folders are moving to a root of their own, and the CLI learns to read both roots in a release that ships ahead of the move, so the binary a session already holds knows where to look by the time a tree relocates. Creating under the new root before then would write records to a root whose ignore line may not have reached a project yet, and it would split one project's records across two roots with no verb able to reconcile them. The move flips the default and nothing else.
13
13
 
14
- A caller never spells a record root by hand for the same reason. A path written as `.claude/plans/...` resolves against one root and reports nothing when it is wrong, which is the quiet failure this ordering exists to prevent: a stale binary meeting a moved layout, writing to the old path, and reporting success. Read a folder through the verb that owns it, and where a skill needs the path itself, take it from that verb's record rather than composing one.
14
+ A caller never spells a record root by hand for the same reason. A path written as `.canon/plans/...` resolves against one root and reports nothing when it is wrong, which is the quiet failure this ordering exists to prevent: a stale binary meeting a moved layout, writing to the old path, and reporting success. Read a folder through the verb that owns it, and where a skill needs the path itself, take it from that verb's record rather than composing one.
15
15
 
16
16
  A refusal names every root it looked at, so a message reading `no-folder` says where a write would land as well as where the read failed.
17
17
 
@@ -126,7 +126,7 @@ The reading carries two windows rather than one, at 7 and 30 days. A single wind
126
126
 
127
127
  Nothing fails on a number here. A record folder has no correct size, so the reading is a number to notice rather than a threshold to gate, and the point of the verb is that the next reading is taken by a command instead of by someone remembering to count the folder. The memory pen went from 44 entries to 236 between two readings taken by hand two weeks apart, which is the measurement this replaces.
128
128
 
129
- The scratch folder is read here and skipped by a backup, because deletable without loss is not the same as empty. The routing handoffs and the memory archive both sit there and both accumulate. `.claude/.records.git` stays out because it is the backup history rather than a record, and `.claude/worktrees/` stays out because each entry is a checkout of the project with its own removal verb, and one of them outweighs every record folder combined.
129
+ The scratch folder is read here and skipped by a backup, because deletable without loss is not the same as empty. The routing handoffs and the memory archive both sit there and both accumulate. `.canon/.records.git` stays out because it is the backup history rather than a record, and `.claude/worktrees/` stays out because each entry is a checkout of the project with its own removal verb, and one of them outweighs every record folder combined.
130
130
 
131
131
  The window counts read `mtime`, so what they report is a file written inside the window rather than one created there. An entry edited long after it landed reads as recent, which overstates growth and never understates it, and these folders are append-mostly so the two readings agree on nearly every file. The one reading that is wrong rather than early is a machine restored by `canon records pull`, which resets the work tree hard and re-dates every file it writes, so a window taken there counts the restore. Nothing on the filesystem separates the two, since a restored file is new by every stamp it carries.
132
132
 
@@ -142,7 +142,7 @@ canon records push --json
142
142
  canon records pull
143
143
  ```
144
144
 
145
- The backed folders are `diagrams`, `groundwork`, `intake`, `memory`, `plans`, `proposals`, `review`, `tasks`, and `teach`, all under whichever record root the project carries. Eight of them are the Claude ignore group the claude manifest ships, minus three entries: `.claude/.tmp`, which is deletable without loss, `.claude/worktrees/`, whose contents belong to the project repository already, and `.claude/.records.git/`, which is the history the rest are pushed into. `diagrams` is the one that group does not carry at all, since a target still tracks its own copies, which is why the list is spelled out rather than derived. Each name is a top-level record folder and every archive sits inside the one it archives, so the list stays at one entry per surface however many archives appear. It is a constant rather than configuration, and it deliberately does not match the six record kinds `validate` hardcodes.
145
+ The backed folders are `diagrams`, `groundwork`, `intake`, `memory`, `plans`, `proposals`, `review`, `tasks`, and `teach`, all under whichever record root the project carries. They are the record root's own entries less three: `tmp`, which is deletable without loss, `.claude/worktrees/`, whose contents belong to the project repository already, and `.records.git/`, which is the history the rest are pushed into. Nothing bounds the list from outside any more, since the claude manifest ships one `.canon/` root entry and names no folder, so spelling the nine out is what keeps a record folder added later from silently entering the payload. Each name is a top-level record folder and every archive sits inside the one it archives, so the list stays at one entry per surface however many archives appear. It is a constant rather than configuration, and it deliberately does not match the six record kinds `validate` hardcodes.
146
146
 
147
147
  Records are gitignored by design, so the history lives in a second git directory at `.records.git` inside the record root, with that root as its work tree. Both resolve off the root together rather than folder by folder, since a history opened at one root beside a work tree at the other would stage the deletion of every folder a move relocated. Every path stays where it is, which is what a separate checkout could not do. The verbs stage the nine folders by explicit pathspec with `--force`, so nothing outside them can enter the index however the ignore rules read, and the project working tree and its index are never touched. Each pathspec is a bare folder name and git reads it against the current directory rather than against the work tree the same call names, so the invocation carries `-C` at the work tree beside the other two flags. That is what lets either verb run from a linked worktree under `.claude/worktrees/`, which sits inside the records work tree and would otherwise prefix every name with its own path.
148
148
 
@@ -151,12 +151,12 @@ Records are gitignored by design, so the history lives in a second git directory
151
151
  A person creates the records repository once per machine, and the verbs refuse with the commands when it is absent:
152
152
 
153
153
  ```bash
154
- git --git-dir=.claude/.records.git init
155
- git --git-dir=.claude/.records.git remote add origin <private-repo-url>
156
- printf '.claude/.records.git/\n' >> .gitignore
154
+ git --git-dir=.canon/.records.git init
155
+ git --git-dir=.canon/.records.git remote add origin <private-repo-url>
156
+ printf '.canon/\n' >> .gitignore
157
157
  ```
158
158
 
159
- The ignore line is repeated here rather than left to the install, because the person running these commands is the one who creates the directory and the rule is worth reading beside the command that needs it. The claude manifest ships `.claude/.records.git/` as the first entry of its group, so a project that ran `canon claude sync` already carries it and this line is a no-op there. What the group buys is the project that never sets records up: it holds a rule for a directory it will never create, which costs nothing, and the backed-folder derivation above subtracts that entry from the group rather than from a list it does not sit in.
159
+ The ignore line is repeated here rather than left to the install, because the person running these commands is the one who creates the directory and the rule is worth reading beside the command that needs it. The claude manifest ships `.canon/` too, so a project that ran `canon tooling sync` already carries it and this line is a no-op there. One root entry covers the history and every record beside it, which is what makes the rule worth stating once rather than per folder.
160
160
 
161
161
  Point it at a private repository, and at one that is not a remote of the project. Records carry the memory pen, the review reports, and the groundwork trails, so a public project publishes all of it to anyone who fetches all refs. `push` compares the configured origin against every remote of the project and refuses on a match. A read of that list which fails refuses as well, since an empty list clears the comparison for every origin and a gate that passes on its own failure is no gate.
162
162
 
@@ -164,7 +164,8 @@ Point it at a private repository, and at one that is not a remote of the project
164
164
 
165
165
  | Reason | What fired |
166
166
  | ------------------- | ------------------------------------------------------------------------------------ |
167
- | `no-repository` | No `.claude/.records.git`, answered with the two setup commands |
167
+ | `split-roots` | Record folders sit under both roots, so the resolved work tree is not the whole set |
168
+ | `no-repository` | No `.canon/.records.git`, answered with the two setup commands |
168
169
  | `no-remote` | The records history has no `origin` |
169
170
  | `remote-unreadable` | The project's own remotes could not be read, so the shared-origin gate could not run |
170
171
  | `remote-shared` | The records origin is also a remote of the project |
@@ -173,6 +174,8 @@ Point it at a private repository, and at one that is not a remote of the project
173
174
  | `local-ahead` | `pull` found local commits that never reached the origin |
174
175
  | `git-failed` | A git call failed, with its stderr in the message |
175
176
 
177
+ `split-roots` runs ahead of every gate below it and fires on a half-migrated tree, which is what a `canon migrate records` run that failed partway leaves. `recordRoot` answers for the whole tree on the first root that exists, so a folder left at the old root is absent from the work tree while the records index still names it, and an unguarded `add -A` would stage its deletion and drop it from the remote on the next push. Finish the move, or put the stranded folders back beside the others.
178
+
176
179
  The two `pull` refusals exist because the directions are not symmetric. A push only adds, while a pull onto a machine holding work that never left it would discard that work. Resolve either by running `push` first, or by moving the local folders aside. A machine holding none of the ten has nothing to lose, so a restore onto a fresh checkout runs straight through.
177
180
 
178
181
  ### When it runs
@@ -38,9 +38,9 @@ A rule stated in two skill bodies and never in `CLAUDE.md` or another rule is ou
38
38
 
39
39
  Matching is recall-first and keyed on shared distinctive tokens rather than on a phrase two surfaces spell the same way. The case this exists for was one rule written three different ways, so a near-exact matcher would ship a cheap report blind to the defect it was built for.
40
40
 
41
- A token is distinctive when it appears in at most 20 of the corpus's statements, which is under one percent of them. `.claude/plans/` sits at 14 and is the anchor the motivating case turns on, while `file` sits at 371 and would match most of the tree.
41
+ A token is distinctive when it appears in at most 20 of the corpus's statements, which is under one percent of them. `.canon/plans/` sits at 14 and is the anchor the motivating case turns on, while `file` sits at 371 and would match most of the tree.
42
42
 
43
- A backticked token counts double. An author marking a span as code named an identifier rather than describing one, so `.claude/plans/archive/` says more about what a statement governs than any two prose words do. Two statements are one rule when their shared anchors reach a weight of 3.
43
+ A backticked token counts double. An author marking a span as code named an identifier rather than describing one, so `.canon/plans/archive/` says more about what a statement governs than any two prose words do. Two statements are one rule when their shared anchors reach a weight of 3.
44
44
 
45
45
  Every record names the anchors its match rested on, so a reader can weigh a finding instead of taking it.
46
46
 
@@ -63,7 +63,7 @@ Two classes resolve to nothing and are right to. Both are reported by name rathe
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 `.claude/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/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.
67
67
 
68
68
  ## Why the glob half reads one corpus
69
69
 
@@ -68,6 +68,6 @@ A skill pairs to a scenario by filename, `<category>-<command>` first and bare `
68
68
 
69
69
  `exempt` means no arm should be written and holds only with a reason, declared in `scripts/sandbox/exempt.toml` and limited to a harness limit the checker cannot reach past or a skill that writes no artifact. An armed arm outranks an exemption. An exemption naming no shipped skill, or naming one an arm now asserts, exits 1 without `--strict`. Each armed arm reports as `<category>:<command>/<arm>`, so two same-named arms under different scenarios stay distinct.
70
70
 
71
- `scripts/sandbox/run.sh` calls this after a headless run and merges the verdict into the envelope it prints. It also writes that merged record to `.claude/.tmp/sandbox-runs/<target>-<arm>-<timestamp>.json` with a `writes` array appended, and logs the path on stderr. Both fields are what a later re-score needs, since `--envelope` and `--writes` read files the run deletes on exit.
71
+ `scripts/sandbox/run.sh` calls this after a headless run and merges the verdict into the envelope it prints. It also writes that merged record to `.canon/tmp/sandbox-runs/<target>-<arm>-<timestamp>.json` with a `writes` array appended, and logs the path on stderr. Both fields are what a later re-score needs, since `--envelope` and `--writes` read files the run deletes on exit.
72
72
 
73
73
  Two more fields ride alongside the verdict rather than inside it. `escapes` lists what the run wrote under a watched toolkit root, which the verdict cannot assert over because those files sit outside the sandbox tree. `sessions` reports the nested-dispatch bound, carrying `watched` for whether the client's session registry was there to read, `new` for the records that appeared while the run was in flight, and `reap` for what the run found in the session's process group afterwards. Neither field fails a run on its own.
@@ -7,7 +7,7 @@ description: Selecting a shipped task by stem or pull request, recording a numbe
7
7
 
8
8
  ## Archive
9
9
 
10
- `canon tasks archive` moves a shipped task from `.claude/tasks/` into `.claude/tasks/archive/`, drops its row from `priority.md`, and regenerates the board index. The three run as one unit, so the attended and unattended callers cannot archive differently.
10
+ `canon tasks archive` moves a shipped task from `.canon/tasks/` into `.canon/tasks/archive/`, drops its row from `priority.md`, and regenerates the board index. The three run as one unit, so the attended and unattended callers cannot archive differently.
11
11
 
12
12
  Name the task by its filename stem, or by the pull request it carries:
13
13
 
@@ -51,7 +51,7 @@ canon tasks archive --pull-request 673 --json | jq -r 'if .ok then .task else .r
51
51
 
52
52
  The record carries `location`, one of `unstated`, `live`, `archived`, or `outside`, and `citedBy`, the other live tasks whose `Plan:` line lands on the same file. Exit codes: `0` read, `1` refused with `no-board` or `no-match`.
53
53
 
54
- The target resolves against the board folder and against the project root both, so `../plans/x.md` and `.claude/plans/x.md` land on the same file and one plan two tasks spelled differently counts once. Containment is tested at both record roots rather than at the one this tree resolves at, since a line somebody wrote against a root the tree has since left is still a path into the plans folder, and reading it as outside would report a shipped plan as still live. `docs/agents/records.md` states the read order.
54
+ The target resolves against the board folder and against the project root both, so `../plans/x.md` and `.canon/plans/x.md` land on the same file and one plan two tasks spelled differently counts once. Containment is tested at both record roots rather than at the one this tree resolves at, since a line somebody wrote against a root the tree has since left is still a path into the plans folder, and reading it as outside would report a shipped plan as still live. `docs/agents/records.md` states the read order.
55
55
 
56
56
  `canon tasks archive` gates on this same answer, so a caller wanting the count reads it here rather than scanning the board. The `claude-docs` plans sweep is the exception and still states the rule in its own body, because a plugin skill reaches a target on merge while the CLI reaches one on release, so a sweep calling a verb the installed `canon` predates gets no record back and archives nothing.
57
57
 
@@ -71,13 +71,13 @@ Name the plan by its path or by its slug, which resolve to the same file:
71
71
 
72
72
  ```bash
73
73
  canon tasks plan-answers dispatch-answer-gate
74
- canon tasks plan-answers .claude/plans/feature-dispatch-answer-gate.md
74
+ canon tasks plan-answers .canon/plans/feature-dispatch-answer-gate.md
75
75
  canon tasks plan-answers ../plans/feature-dispatch-answer-gate.md
76
76
  ```
77
77
 
78
- A relative path resolves against the project root first and against `.claude/tasks/` second. The third form above is what a board row writes, since its link is relative to the board, and a dispatcher copying the reference out of the row it is dispatching has that spelling to hand rather than either of the other two. A refusal names every base it looked under.
78
+ A relative path resolves against the project root first and against `.canon/tasks/` second. The third form above is what a board row writes, since its link is relative to the board, and a dispatcher copying the reference out of the row it is dispatching has that spelling to hand rather than either of the other two. A refusal names every base it looked under.
79
79
 
80
- `canon tasks plan-citations` reads a task's `Plan:` line against those same two bases in the opposite order, and tests that the target lands under the live plans folder, which this verb does not. Both answer the same file for every spelling a board writes. Liveness is a separate refusal here: a plan resolving inside `.claude/plans/archive/` returns `archived` rather than a launchable reading, since it answers every question and describes work that already shipped.
80
+ `canon tasks plan-citations` reads a task's `Plan:` line against those same two bases in the opposite order, and tests that the target lands under the live plans folder, which this verb does not. Both answer the same file for every spelling a board writes. Liveness is a separate refusal here: a plan resolving inside `.canon/plans/archive/` returns `archived` rather than a launchable reading, since it answers every question and describes work that already shipped.
81
81
 
82
82
  | Option | Effect |
83
83
  | --------------- | ------------------------------------------- |
@@ -115,7 +115,7 @@ canon tasks pull-request 673 --plan worktree-scratch-routing --json
115
115
  | `--json` | Emit a machine-readable record on stdout |
116
116
  | `--root <path>` | Board root, defaulting to the main worktree |
117
117
 
118
- A plan is matched on the token both spellings share, so `worktree-scratch-routing`, `feature-worktree-scratch-routing`, and `.claude/plans/feature-worktree-scratch-routing.md` all select the same task. The `action` field reports `added`, `corrected`, or `unchanged`, which makes a rerun against the same number safe.
118
+ A plan is matched on the token both spellings share, so `worktree-scratch-routing`, `feature-worktree-scratch-routing`, and `.canon/plans/feature-worktree-scratch-routing.md` all select the same task. The `action` field reports `added`, `corrected`, or `unchanged`, which makes a rerun against the same number safe.
119
119
 
120
120
  Exit codes: `0` recorded, `1` refused. The `reason` field carries `no-board`, `no-match`, or `ambiguous`. `git-pr` skips silently on those three, because each is a case where a guessed write would archive the wrong task once the branch merges.
121
121
 
@@ -5,13 +5,13 @@ description: Listing learning workspaces and the ordinal a new one takes, openin
5
5
 
6
6
  # Teach
7
7
 
8
- Learning workspaces sit under `.claude/teach/<nn>-<topic>/`, and `standards/teach.md` fixes their layout, naming, and file formats, apart from the glossary, whose shape `standards/glossary.md` fixes, cited from the `claude-teach` skill, so it travels with the file wherever a promotion lands it. Every verb here resolves that folder against the main worktree root rather than against the working directory, so a session standing in a linked worktree reaches the one workspace the learner has rather than opening a second.
8
+ Learning workspaces sit under `.canon/teach/<nn>-<topic>/`, and `standards/teach.md` fixes their layout, naming, and file formats, apart from the glossary, whose shape `standards/glossary.md` fixes, cited from the `claude-teach` skill, so it travels with the file wherever a promotion lands it. Every verb here resolves that folder against the main worktree root rather than against the working directory, so a session standing in a linked worktree reaches the one workspace the learner has rather than opening a second.
9
9
 
10
10
  That root resolution is also why the writing verbs exist at all. The file-editing tools refuse a main-root path from a linked worktree and offer a worktree copy instead, and a caller naming only the destination reports a success that did not happen. A whole-file create still goes out as a shell heredoc. Changing a line inside a file that already exists has no shell route, because the stream editors are banned, so `resource` and `glossary` are the route for the two files a running workspace edits.
11
11
 
12
12
  ## List
13
13
 
14
- `canon teach list` reports the workspaces under `.claude/teach/`, or what one workspace holds. It reads and never writes.
14
+ `canon teach list` reports the workspaces under `.canon/teach/`, or what one workspace holds. It reads and never writes.
15
15
 
16
16
  ```bash
17
17
  canon teach list
@@ -134,7 +134,7 @@ The order is drawn here rather than instructed, and that is the point of the ver
134
134
  No `canon teach` verb serves the workspace. `canon serve` does, taking the teach root as its directory and the workspace contents page as its entry:
135
135
 
136
136
  ```bash
137
- canon serve .claude/teach --entry 03-fde-system-design/index.html --json
137
+ canon serve .canon/teach --entry 03-fde-system-design/index.html --json
138
138
  ```
139
139
 
140
140
  It stays general rather than becoming `canon teach serve`, because nothing about serving a directory is specific to a learning workspace, and the same verb carries a slide render and a design preview.
@@ -145,7 +145,7 @@ Read `url` off the record rather than building one from the port that was asked
145
145
 
146
146
  | Reason | Raised when |
147
147
  | -------------- | --------------------------------------------------------------- |
148
- | `no-teach` | The root carries no `.claude/teach/` folder |
148
+ | `no-teach` | The root carries no `.canon/teach/` folder |
149
149
  | `no-workspace` | No workspace matches the topic, with the folder names as detail |
150
150
  | `ambiguous` | Two workspaces claim one topic, with both names as detail |
151
151
  | `exists` | A workspace already covers the topic an open names |