@erclx/canon 5.38.0 → 5.40.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 (246) hide show
  1. package/README.md +5 -5
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/SKILL.md +12 -9
  4. package/claude/skills/auto-ship/SKILL.md +43 -23
  5. package/claude/skills/backlog-triage/SKILL.md +62 -53
  6. package/claude/skills/backlog-triage/references/apply.md +69 -0
  7. package/claude/skills/build-in-slices/SKILL.md +8 -4
  8. package/claude/skills/canon-cli/SKILL.md +5 -1
  9. package/claude/skills/canon-feedback/SKILL.md +18 -8
  10. package/claude/skills/canon-feedback-triage/SKILL.md +10 -3
  11. package/claude/skills/canvas-board/SKILL.md +37 -18
  12. package/claude/skills/ci-workflow/SKILL.md +5 -4
  13. package/claude/skills/code-craft/SKILL.md +10 -6
  14. package/claude/skills/codebase-layout/SKILL.md +3 -5
  15. package/claude/skills/context-fold/REQUIREMENT.md +4 -4
  16. package/claude/skills/context-fold/SKILL.md +42 -23
  17. package/claude/skills/context-fold/references/context-refresh.md +4 -3
  18. package/claude/skills/context-fold/references/context.md +79 -64
  19. package/claude/skills/create-rule/SKILL.md +7 -3
  20. package/claude/skills/create-skill/SKILL.md +27 -7
  21. package/claude/skills/create-skill/references/skill-reference.md +41 -17
  22. package/claude/skills/decision-escalate/SKILL.md +8 -5
  23. package/claude/skills/deploy-app/SKILL.md +1 -1
  24. package/claude/skills/deprecation-migration/SKILL.md +12 -4
  25. package/claude/skills/design-taste/SKILL.md +17 -11
  26. package/claude/skills/docs-sync/REQUIREMENT.md +1 -0
  27. package/claude/skills/docs-sync/SKILL.md +17 -6
  28. package/claude/skills/docs-sync/references/docs.md +54 -21
  29. package/claude/skills/docs-sync/references/readme.md +32 -8
  30. package/claude/skills/document-health/SKILL.md +3 -4
  31. package/claude/skills/draft-and-pick/SKILL.md +73 -47
  32. package/claude/skills/draft-doc/SKILL.md +9 -4
  33. package/claude/skills/draft-doc/references/context.md +4 -4
  34. package/claude/skills/draft-doc/references/docs.md +1 -0
  35. package/claude/skills/draft-doc/references/wiki.md +3 -2
  36. package/claude/skills/draft-figure/SKILL.md +9 -7
  37. package/claude/skills/draft-identity/SKILL.md +24 -52
  38. package/claude/skills/draft-identity/references/card-route.md +46 -0
  39. package/claude/skills/draft-ready/SKILL.md +19 -7
  40. package/claude/skills/draft-screencast/SKILL.md +6 -112
  41. package/claude/skills/draft-screencast/references/beats-template.md +119 -0
  42. package/claude/skills/draft-slides/SKILL.md +9 -7
  43. package/claude/skills/draft-slides/references/deck.md +183 -0
  44. package/claude/skills/git-branch/SKILL.md +3 -3
  45. package/claude/skills/git-commit/SKILL.md +5 -3
  46. package/claude/skills/git-followup/SKILL.md +16 -9
  47. package/claude/skills/git-issue/SKILL.md +6 -5
  48. package/claude/skills/git-pr/SKILL.md +56 -44
  49. package/claude/skills/git-pr/references/evidence.md +2 -2
  50. package/claude/skills/git-pr/references/final-command.md +1 -1
  51. package/claude/skills/git-pr/references/labels.md +1 -1
  52. package/claude/skills/git-pr/references/publish.md +1 -1
  53. package/claude/skills/git-ship/SKILL.md +43 -34
  54. package/claude/skills/git-ship/references/ci-watch.md +31 -0
  55. package/claude/skills/git-split/SKILL.md +28 -100
  56. package/claude/skills/git-split/references/independent.md +40 -0
  57. package/claude/skills/git-split/references/stacked.md +73 -0
  58. package/claude/skills/git-stage/SKILL.md +9 -7
  59. package/claude/skills/git-worktree/SKILL.md +27 -49
  60. package/claude/skills/git-worktree/references/cleanup.md +55 -0
  61. package/claude/skills/markdown-craft/SKILL.md +2 -2
  62. package/claude/skills/markdown-craft/references/markdown.md +82 -53
  63. package/claude/skills/plan-feature/SKILL.md +52 -46
  64. package/claude/skills/plan-feature/references/batches.md +46 -0
  65. package/claude/skills/plan-groundwork/SKILL.md +60 -74
  66. package/claude/skills/plan-groundwork/references/output.md +48 -0
  67. package/claude/skills/plan-intake/SKILL.md +28 -21
  68. package/claude/skills/plan-intake-answer/SKILL.md +14 -13
  69. package/claude/skills/read-frames/SKILL.md +2 -0
  70. package/claude/skills/record-screencast/SKILL.md +31 -11
  71. package/claude/skills/review-address/SKILL.md +76 -158
  72. package/claude/skills/review-address/references/rebase-only.md +34 -0
  73. package/claude/skills/review-craft/SKILL.md +27 -11
  74. package/claude/skills/review-pr/SKILL.md +91 -40
  75. package/claude/skills/review-ui/SKILL.md +63 -37
  76. package/claude/skills/review-ui/references/hosted-preview.md +30 -0
  77. package/claude/skills/role-orchestrator/SKILL.md +105 -74
  78. package/claude/skills/role-orchestrator/references/orchestrator-filing.md +26 -0
  79. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
  80. package/claude/skills/role-planner/SKILL.md +66 -64
  81. package/claude/skills/role-reviewer/SKILL.md +57 -46
  82. package/claude/skills/role-worker/SKILL.md +68 -63
  83. package/claude/skills/search-craft/SKILL.md +7 -5
  84. package/claude/skills/session-compact/SKILL.md +16 -11
  85. package/claude/skills/session-map/SKILL.md +31 -10
  86. package/claude/skills/session-relay/SKILL.md +32 -65
  87. package/claude/skills/session-resume/SKILL.md +18 -13
  88. package/claude/skills/session-worktree/SKILL.md +70 -42
  89. package/claude/skills/systematic-debugging/SKILL.md +6 -1
  90. package/claude/skills/target-check/SKILL.md +13 -6
  91. package/claude/skills/target-setup/SKILL.md +39 -27
  92. package/claude/skills/task-board/SKILL.md +68 -60
  93. package/claude/skills/task-board/references/board.md +1 -1
  94. package/claude/skills/task-board/references/create-report.md +36 -0
  95. package/claude/skills/task-board/references/tasks.md +1 -1
  96. package/claude/skills/task-complete/SKILL.md +20 -18
  97. package/claude/skills/teach-workspace/SKILL.md +61 -33
  98. package/claude/skills/test-craft/SKILL.md +2 -1
  99. package/claude/skills/test-first/SKILL.md +15 -9
  100. package/claude/skills/ui-checklist/SKILL.md +18 -16
  101. package/claude/skills/ux-audit/SKILL.md +9 -10
  102. package/claude/skills/ux-measure/SKILL.md +23 -19
  103. package/claude/skills/video-craft/SKILL.md +15 -7
  104. package/claude/skills/write-human/SKILL.md +9 -1
  105. package/claude/skills/youtube-transcripts/SKILL.md +2 -1
  106. package/docs/agents/audits.md +61 -60
  107. package/docs/agents/context-audit.md +40 -58
  108. package/docs/agents/index.md +11 -61
  109. package/docs/agents/indexes.md +25 -29
  110. package/docs/agents/install-and-sync.md +125 -235
  111. package/docs/agents/markdown-audit.md +54 -96
  112. package/docs/agents/migrate.md +41 -16
  113. package/docs/agents/output-shape.md +60 -45
  114. package/docs/agents/overview.md +36 -12
  115. package/docs/agents/records.md +37 -79
  116. package/docs/agents/sessions-transfer.md +26 -70
  117. package/docs/agents/targets.md +25 -77
  118. package/docs/target/index.md +0 -1
  119. package/docs/target/migrations.md +57 -66
  120. package/docs/target/projects.md +36 -78
  121. package/docs/target/scenarios.md +9 -19
  122. package/docs/target/sync.md +27 -68
  123. package/docs/workflow/ai-workflow.md +22 -68
  124. package/docs/workflow/operating-model.md +32 -186
  125. package/docs/workflow/parallel-features.md +29 -66
  126. package/docs/workflow/skill-map-on-request.md +22 -24
  127. package/docs/workflow/skill-map.md +72 -76
  128. package/docs/workflow/visual-design-workflow.md +25 -118
  129. package/docs/workflow/zshrc-aliases.md +18 -37
  130. package/package.json +1 -1
  131. package/src/canvas/client/inspector/fields.css +4 -0
  132. package/src/canvas/index.html +6 -0
  133. package/src/claude/skills-reach.ts +2 -2
  134. package/src/cli/ui.ts +3 -1
  135. package/src/commands/canvas.ts +23 -0
  136. package/src/commands/capture.ts +14 -0
  137. package/src/commands/claude.ts +14 -1
  138. package/src/commands/context.ts +106 -7
  139. package/src/commands/demo.ts +22 -0
  140. package/src/commands/deps.ts +6 -0
  141. package/src/commands/driver.ts +28 -2
  142. package/src/commands/gov.ts +52 -18
  143. package/src/commands/hooks.ts +63 -0
  144. package/src/commands/intake.ts +17 -0
  145. package/src/commands/labels.ts +38 -5
  146. package/src/commands/markdown.ts +169 -32
  147. package/src/commands/pr.ts +160 -43
  148. package/src/commands/records.ts +8 -0
  149. package/src/commands/secrets.ts +6 -0
  150. package/src/commands/sessions.ts +8 -0
  151. package/src/commands/tasks.ts +52 -0
  152. package/src/commands/teach.ts +13 -0
  153. package/src/commands/upstream.ts +25 -1
  154. package/src/commands/worktrees.ts +5 -0
  155. package/src/context/audit.ts +274 -5
  156. package/src/context/classify/prompts.ts +1 -1
  157. package/src/context/folders.ts +1 -1
  158. package/src/context/gate.ts +23 -7
  159. package/src/context/shape-gated.ts +30 -0
  160. package/src/counts/scan.ts +1 -1
  161. package/src/design/components.ts +1 -1
  162. package/src/design/document.ts +1 -1
  163. package/src/design/tokens.ts +1 -1
  164. package/src/gate/boundaries.ts +4 -1
  165. package/src/gate/measures.ts +16 -191
  166. package/src/gate/stages.ts +14 -30
  167. package/src/git/env.ts +6 -0
  168. package/src/hooks/reply-links.ts +110 -0
  169. package/src/markdown/density.ts +258 -0
  170. package/src/markdown/gate.ts +57 -18
  171. package/src/markdown/reference-shape.ts +103 -0
  172. package/src/markdown/scope.ts +162 -0
  173. package/src/markdown/structure.ts +9 -59
  174. package/src/pr/lift.ts +153 -19
  175. package/src/pr/painted.ts +87 -0
  176. package/src/roots/record.ts +18 -1
  177. package/src/sandbox/census.ts +1 -1
  178. package/src/sandbox/headless/run.ts +3 -2
  179. package/src/sandbox/headless/watch.ts +3 -4
  180. package/src/shipped/references.ts +1 -1
  181. package/src/slides/convert/master.ts +3 -0
  182. package/src/sync/format.ts +46 -0
  183. package/src/sync/stamp.ts +6 -0
  184. package/tooling/astro/reference.md +2 -2
  185. package/tooling/base/reference.md +1 -1
  186. package/tooling/claude/seeds/.claude/hooks/reply-links.sh +72 -0
  187. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +22 -1
  188. package/tooling/claude/seeds/.claude/settings.json +4 -0
  189. package/tooling/go/reference.md +1 -1
  190. package/tooling/nextjs/reference.md +1 -1
  191. package/tooling/php/reference.md +1 -1
  192. package/tooling/python/reference.md +1 -1
  193. package/tooling/vite-react/reference.md +1 -1
  194. package/tooling/web/reference.md +1 -1
  195. package/docs/agents/canvas.md +0 -86
  196. package/docs/agents/capture.md +0 -65
  197. package/docs/agents/census.md +0 -23
  198. package/docs/agents/commands.md +0 -154
  199. package/docs/agents/comments.md +0 -34
  200. package/docs/agents/context-audit-checks.md +0 -90
  201. package/docs/agents/context-classify.md +0 -101
  202. package/docs/agents/counts.md +0 -74
  203. package/docs/agents/demo.md +0 -140
  204. package/docs/agents/docs.md +0 -27
  205. package/docs/agents/drift-surfaces.md +0 -201
  206. package/docs/agents/driver.md +0 -103
  207. package/docs/agents/gate.md +0 -90
  208. package/docs/agents/gov-detect.md +0 -47
  209. package/docs/agents/hooks.md +0 -58
  210. package/docs/agents/intake.md +0 -81
  211. package/docs/agents/key-changes.md +0 -116
  212. package/docs/agents/label-coverage.md +0 -86
  213. package/docs/agents/markdown-audit-weight.md +0 -81
  214. package/docs/agents/pr-evidence-layout.md +0 -70
  215. package/docs/agents/pr-evidence.md +0 -273
  216. package/docs/agents/pr-frames.md +0 -131
  217. package/docs/agents/pr-lift.md +0 -132
  218. package/docs/agents/pr-local.md +0 -111
  219. package/docs/agents/pr-preview.md +0 -155
  220. package/docs/agents/pr-reads.md +0 -111
  221. package/docs/agents/pr-tick.md +0 -72
  222. package/docs/agents/records-push.md +0 -61
  223. package/docs/agents/records-stale.md +0 -40
  224. package/docs/agents/records-validate.md +0 -57
  225. package/docs/agents/restated.md +0 -83
  226. package/docs/agents/routing.md +0 -61
  227. package/docs/agents/rule-citations.md +0 -90
  228. package/docs/agents/sandbox.md +0 -126
  229. package/docs/agents/scripting.md +0 -142
  230. package/docs/agents/sessions.md +0 -128
  231. package/docs/agents/skills-audit.md +0 -120
  232. package/docs/agents/skills-reach.md +0 -64
  233. package/docs/agents/slides.md +0 -176
  234. package/docs/agents/state-scoped-risk.md +0 -105
  235. package/docs/agents/superseded.md +0 -85
  236. package/docs/agents/tasks-archive.md +0 -83
  237. package/docs/agents/tasks-plans.md +0 -152
  238. package/docs/agents/tasks-validate.md +0 -133
  239. package/docs/agents/tasks.md +0 -123
  240. package/docs/agents/teach-authoring.md +0 -118
  241. package/docs/agents/teach.md +0 -157
  242. package/docs/agents/test-order.md +0 -58
  243. package/docs/agents/upstream.md +0 -94
  244. package/docs/agents/worktrees.md +0 -118
  245. package/docs/target/standards-moved.md +0 -34
  246. package/src/markdown/growth.ts +0 -43
@@ -1,34 +1,59 @@
1
1
  ---
2
2
  title: Migrate
3
- description: What each canon migrate verb moves and rewrites, what it refuses, the markers that keep an old spelling, and the idempotence check a second run gives
3
+ description: What each canon migrate verb moves, the markers that keep an old spelling, and what each one refuses
4
+ kind: reference
4
5
  ---
5
6
 
6
7
  # Migrate
7
8
 
8
- `canon migrate <verb>` moves a project off a retired name or layout. Each verb reports its plan and writes nothing until `--write` is passed, and each takes `--json`. Every verb but `rename` takes `--root`.
9
+ `canon migrate <verb>` moves a project off a retired name or layout. Each verb reports its plan and writes nothing until `--write`, takes `--json`, and, except `rename`, takes `--root`. A second run rewrites nothing, which confirms the exclusions and markers fired.
9
10
 
10
- `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.
11
+ | Verb | Moves |
12
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
13
+ | `rename` | The retired `aitk` name. `--scope target` rewrites toolkit-owned folders only. |
14
+ | `skill-names` | Plugin skills off their `claude-` prefix and other retired names, with every citation |
15
+ | `records` | Session records from `.claude/` to `.canon/`, with every tracked citation |
16
+ | `surface-roots` | `context/`, `wireframes/`, `ARCHITECTURE.md`, `REQUIREMENTS.md`, `DESIGN.md`, and the stamp folder from `.claude/` to `canon/`, by `git mv` |
17
+ | `record-tree` | Citations inside `.canon/` folders a session still follows: `diagrams`, `memory`, `plans`, `proposals`, `review`, `tasks`, `teach` |
18
+ | `rule-layout` | A flat installed-rule layout onto the `canon/`-wrapped one |
19
+ | `scratch-evidence` | A folder a durable record cites out of `.tmp/`, with its citations |
20
+ | `record-layout` | Review receipts, memory archives, feedback, renders, picks, and evidence into their current folders |
11
21
 
12
- `migrate skill-names` moves the plugin skills that carried a `claude-` prefix onto their two-word names and rewrites every citation of one. It carries two plain renames as well, where a skill carried no prefix and changed its name because the old one described a job it no longer does, landing on `ui-checklist` and `context-fold`, and one merge, where the five per-kind document drafters land on `draft-doc`. It carries one namespaced-only rename, `canvas` to `canvas-board`, keyed on `canon:canvas` and `skills/canvas` because a bare `canvas` key would rewrite the verb, its source folder, and its record folder. It leaves the pointer answering under the fold skill's old name in place. It shares its engine with `migrate rename` and differs in what it is scanning for: a skill name is a whole name rather than a word stem, so it matches only where the word ends, which is what keeps a longer word that merely opens with a skill name, such as `claude-worktrees`, out of it. It takes no `--scope`, since the skill folders it moves are authored in the toolkit and no target holds a copy of that catalog, which leaves a target's own citations of a renamed skill as the whole of what it rewrites there. The changelog and the eval transcripts are passed over on the argument the other sweep already carries, that each records what shipped or what a session ran under whatever name was current then. <!-- canon-keep-retired -->
22
+ - `skill-names` takes no `--scope` and skips the changelog and eval transcripts
23
+ - `surface-roots` leaves `rules/`, `skills/`, `hooks/`, and `settings.json` where Claude Code reads them
24
+ - `rule-layout` classifies each flat file against the stamp as clean, edited, or unclaimed. Clean and edited files move untouched, and an unclaimed name stays.
25
+ - `record-layout` labels each evidence folder `(pick)` or `(evidence)` in the dry run
13
26
 
14
- `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.
27
+ ## Output
15
28
 
16
- 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 nearest non-blank line below it, keeps the old spelling for prose that dates a decision. The records themselves are never swept: everything under `.canon/` and every `.claude/` record folder is passed over and reported as a count on its own line, which is what keeps the run that follows the ignore collapse touching the same files as one before it. Running it twice rewrites nothing, which is the check that the exclusions, the markers, and that skip all fired.
29
+ - The plan lists every citation in scope by file, line number, and line text
30
+ - `record-tree` reports closed groundwork and intake trails, the scratch folder, and backup history as counts and rewrites none
31
+ - `records` reports skipped record folders as a count on its own line
32
+ - A `record-tree` rewrite has no undo in version control, so read the report before `--write`
17
33
 
18
- `migrate surface-roots` moves the tracked surfaces from `.claude/` to `canon/`: `context/`, `wireframes/`, `ARCHITECTURE.md`, `REQUIREMENTS.md`, `DESIGN.md`, and the install stamp folder, respelled `canon/config/` since a project under `canon/` reserves the bare `canon` name for the CLI's own install. Every path is tracked, so each file moves with `git mv` and its history follows it, and every tracked citation of one is rewritten in the same run. `rules/`, `skills/`, `hooks/`, and `settings.json` stay where Claude Code reads them. It reports until `--write` is passed, and a destination already present under `canon/` refuses the whole plan rather than splitting a surface across both roots.
34
+ Three markers keep an old spelling on a line. Each also covers the nearest non-blank line, below it for `canon-keep-record-root` and above it for `canon-keep-surface-root`.
19
35
 
20
- A line carrying `canon-keep-surface-root`, or the nearest non-blank line above it, keeps the old spelling. A rule's frontmatter `paths:` glob, a citation shaped like a path into another repository, and one inside a dated paragraph are reported by file and line and left alone. Running it twice rewrites nothing, which is the idempotence check. A project that never runs it keeps resolving each surface under `.claude/`, since every reader takes whichever root holds the entry.
36
+ | Marker | Keeps |
37
+ | ------------------------- | ---------------------------------------------- |
38
+ | `canon-keep-record-root` | The old record folder spelling |
39
+ | `canon-keep-surface-root` | The old `.claude/` surface root |
40
+ | `canon-keep-retired` | A retired name, for `rename` and `skill-names` |
21
41
 
22
- `migrate record-tree` is what reaches the records the sweep above passes over, and the two share no scope. That one enumerates through git, so it sees every tracked file and none of the records, which are gitignored by construction. This one walks `.canon/` itself and is scoped to the folders a session still follows a path into: `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 reported as counts and never rewritten, because a path inside a closed trail sits in a sentence about work that already ended. Run it after the move, since there is no new root to walk before one.
42
+ `surface-roots` also reports and leaves a rule's frontmatter `paths:` glob, a path into another repository, and a citation in a dated paragraph.
23
43
 
24
- Every citation in scope is reported with its file, its line number, and the line text. The record tree is untracked, so a wrong rewrite has no git undo, and that report is what a reader judges before passing `--write`. Marking a line that has to keep the old spelling uses the same `canon-keep-record-root` comment, and a second run rewriting nothing is the idempotence check.
44
+ ## Refusals
25
45
 
26
- `migrate rule-layout` moves a target still on the flat installed-rule layout onto the `canon/`-wrapped one. It classifies each flat file against the target's own recorded stamp: an unchanged hash is clean, a changed one is edited, and a name neither the stamp nor the current rule catalog can vouch for is unclaimed and left in place. Clean and edited files both relocate with their content untouched, since moving a rule is a different decision from re-syncing it. The one renumbered rule reads its destination from a table rather than from its own current name, so it never lands as a second copy under the retired number.
46
+ | Verb | Refuses when |
47
+ | ------------------ | ----------------------------------------------------------------------------------- |
48
+ | `records` | The project does not ignore `.canon/`. Run `canon tooling sync --write` first. |
49
+ | `records` | A record folder already exists at the destination |
50
+ | `surface-roots` | Any destination already exists under `canon/`, refusing the whole plan |
51
+ | `scratch-evidence` | A destination is occupied |
52
+ | `record-layout` | A destination already exists, each move refused on its own |
53
+ | `rule-layout` | A destination holds different bytes. Neither file is touched and other moves apply. |
27
54
 
28
- `migrate scratch-evidence` moves a folder out of `.tmp/` for good rather than repointing a citation in place. A promoted folder is one a durable record, live or archived, cites as its evidence, and no source file under `claude/`, `scripts/`, `src/`, or `governance/` names by path, which is what a script still writing into it fails. It walks every `canon records push` folder rather than the live ones alone, since most of what it repoints sits in `tasks/archive/`, `plans/archive/`, and `groundwork/`. A destination already occupied is a refusal, and a second run rewriting nothing is the same idempotence check `migrate record-tree` uses.
55
+ A `rule-layout` destination holding the same bytes marks a duplicate, which `--write` deletes.
29
56
 
30
- A destination that already holds the same bytes marks the flat file a duplicate, which `--write` deletes rather than moves. One holding different bytes is a real collision: neither file is touched, it is reported by name, and every other planned move still applies independently. Run this before `canon gov install` or `canon gov sync` against a target still on the flat layout, since neither bootstrap verb detects or clears it on its own.
57
+ ## See also
31
58
 
32
- `migrate record-layout` folds two record folders under the memory pen: review receipts move from `.canon/review/memory/` to `.canon/memory/review/`, and retired entries move from `.canon/tmp/memory-archive/` to `.canon/memory/archive/`, backed for the first time since `memory/` is a folder `canon records push` carries and `tmp/` is not. Each move is refused independently when its destination already exists, and the citation repoint sweeps every folder `canon records push` carries, archives included, since an archived receipt still cites the row it retired. The same `canon-keep-record-root` marker and second-run idempotence check apply. A receipt sitting at the flat `review/` root, from before `.canon/review/memory/` existed, matches neither mapped move and is reported by name rather than moved.
33
-
34
- The same verb leaves `.canon/review/` holding only reviews. Feedback moves to `.canon/feedback/`, and the design, board, slides, and diagram renders move to `.canon/tmp/render/<kind>/`, off the records remote since the commands regenerate them. Reference images move to `.canon/picks/references/`. A branch report flattens from `review/branch/review-<slug>.md` to `review/branch-<slug>.md`, and a flat `review/ui-checklist-<slug>.md` moves into `.canon/tmp/handoff/ui-checklist/`. Each folder under `review/evidence/` is decided from what it holds: one directly carrying an `arm-<id>` capture or a `design-handoff.md` is a pick and moves to `.canon/picks/<slug>/`, and any other is evidence and moves to `.canon/evidence/<nn>-<slug>/`. Evidence folders are numbered by the oldest file each holds, continuing past any ordinal `evidence/` already carries. The dry run labels each derived destination `(pick)` or `(evidence)`, so a misfiled folder shows before `--write`, and one filed wrong moves back by hand. A bare mention of an emptied folder, such as `review/evidence/` with no slug or `review/branch/` with no report name, matches no row and stays as written.
59
+ - `install-and-sync.md`: the drift report that names these verbs as remedies
@@ -1,15 +1,13 @@
1
1
  ---
2
2
  title: Output shape
3
- description: Two framed shapes every command renders into, how JSON and --names modes keep stdout clean, and the exit discipline that lets piped output drain
3
+ description: The framed output shape every command renders into, the JSON and color modes, the exit discipline, the runtime catalogs, and a headless invocation per domain
4
4
  ---
5
5
 
6
6
  # Output shape
7
7
 
8
- Every CLI command renders into one of two framed shapes. Data goes to stdout. UI and logs go to stderr.
8
+ Data goes to stdout. UI and logs go to stderr. Help is the exception and prints to stdout so it can be piped.
9
9
 
10
- Help output is the exception. It prints to stdout so it can be piped and grepped.
11
-
12
- ## Data shape (lists, runs, errors)
10
+ ## Frame
13
11
 
14
12
  ```plaintext
15
13
  ┌
@@ -17,54 +15,71 @@ Help output is the exception. It prints to stdout so it can be piped and grepped
17
15
  │
18
16
  ├ Section
19
17
  │ ✓ item
20
- │ ✓ item
21
18
  └
22
19
  ```
23
20
 
24
- Rules:
25
-
26
- - `┌` opens the frame on stderr
27
- - `│ canon <domain>` is the command banner, one per invocation
28
- - `├ Section` headers introduce groups of items. `log_step` produces the blank `│` spacer before each.
29
- - `└` closes the frame on stderr, wired via `trap close_timeline EXIT`
30
- - Errors render as `│ ✗ message` inside the same frame. Never emit a lone error line without a frame.
21
+ - `┌` opens the frame, `│ canon <domain>` is the banner, and `├ Section` groups items
22
+ - `└` closes the frame on stderr
23
+ - An error renders as `│ ✗ message` inside the frame, never as a lone line
24
+ - Help skips the banner and puts `Usage:` directly on `├`
31
25
 
32
- ## Help shape
33
-
34
- ```plaintext
35
- ┌
36
- ├ Usage: canon <domain> [command]
37
- │
38
- │ Commands:
39
- │ ...
40
- └
41
- ```
26
+ A help request naming an unknown subcommand, under a group taking no operand, writes its refusal to stderr and exits 1.
42
27
 
43
- Help skips the banner. The `Usage:` line sits directly on `├`. Help writes to stdout because `--help` is documentation, not runtime UI. `--help --all` on a command prints each block unframed, one after another, and a help request naming an unknown subcommand, under a group that takes no operand of its own, writes its refusal to stderr and exits 1.
28
+ ## JSON and names modes
44
29
 
45
- ## JSON and `--names` modes
30
+ `--json` and `--names` keep stdout pure data. The frame still renders on stderr, and diagnostics reach stderr in every mode. A JSON refusal carries an action and a reason, so branch on the record rather than on stderr.
46
31
 
47
- `--json` and `--names` keep stdout clean and machine-readable. The frame still renders on stderr (open, banner, close) so the stream discipline is consistent across modes. Consumers that only read stdout see pure data.
32
+ A shell profile may wrap `canon` in a function that takes its status from a later command. Where a verb documents a record field, read the field rather than the exit.
48
33
 
49
34
  ## Color
50
35
 
51
- Escape sequences reach a destination that renders them and nowhere else. The question is asked per stream rather than once for the process, so a run piping its data while keeping a terminal on stderr still gets color on the frame.
52
-
53
- Either condition alone turns color off:
54
-
55
- - `NO_COLOR` is set to any non-empty value, whatever that value says
56
- - The destination is not a terminal, which covers a pipe, a file, and a captured session
57
-
58
- The frame survives both. `┌`, `│`, `├`, `└`, and the `✓ ! + - ✗` marks are structure rather than color, and they are what lets a captured run still read as one block. A caller wanting neither the frame nor the color reads `--json` instead.
59
-
60
- Terminal control is a separate question this section does not cover. The cursor and key sequences an interactive prompt writes run only where a terminal already exists.
61
-
62
- ## Process exit
63
-
64
- A command action sets `process.exitCode` and returns. Calling `process.exit()` there ends the process before a stdout write drains, which truncates piped output at the 64K pipe buffer while still reporting the right exit code. Redirecting to a file hides the truncation, so it surfaces only through a pipe, which is what a check has to use to catch it.
65
-
66
- The rule reaches an error path that writes to stderr alone, where the truncation has nothing to cut. Scoping it to the stdout writers was the alternative and it makes the floor depend on a detail that moves, since an action grows a stdout write long after its error branches are written. An action always has a return path, so the requirement costs it a line.
67
-
68
- The exit belongs to a helper that has no caller to unwind through. A prompt inside a promise executor and a wrapper propagating a child process status both qualify, and neither can hand a value back to a caller expecting one. A validation helper called from an action does not: it throws from a `never` return, which keeps its caller exhaustive to the compiler while the action catches and owns the code.
36
+ Escape sequences reach a destination that renders them and nowhere else, decided per stream. `NO_COLOR` set to any non-empty value turns color off, and so does a destination that is not a terminal. The frame glyphs and the `✓ ! + - ✗` marks survive both.
37
+
38
+ ## Exit discipline
39
+
40
+ A command action sets `process.exitCode` and returns. Calling `process.exit()` in an action truncates piped output at the 64K buffer. A file redirect hides the truncation, so test through a pipe.
41
+
42
+ ## Runtime catalogs
43
+
44
+ Discover names at runtime rather than hardcoding them.
45
+
46
+ | Command | Returns |
47
+ | --------------------------------- | ---------------------------------------------------------------- |
48
+ | `canon tooling list --json` | Stacks, extends chain, dep and script counts |
49
+ | `canon gov list --json` | Governance stacks, rule sets, evidence, and unreferenced rules |
50
+ | `canon gov detect --json` | Opt-in rules a target's dependencies and paths propose |
51
+ | `canon claude seeds list --json` | Seed doc sources with content |
52
+ | `canon claude skills list --json` | Plugin skills, descriptions, requirement flag, family vocabulary |
53
+ | `canon docs list --json` | Consumer docs plus per-domain context |
54
+ | `canon audits list --json` | Audits the set runs, the corpus each reads, and whether it gates |
55
+
56
+ - `gov list` expands each stack to its rules and carries `unreferenced`, the rules no stack reaches, which install only through `--add`
57
+ - `claude skills list` reports shipped skills only. Each carries `family` (or `null`) and `requirement`, and a `false` requirement is a gap to close. `--names` and `--by-family` change the render.
58
+ - A skill with unparseable frontmatter returns an empty description rather than failing the listing
59
+
60
+ ## Headless invocation
61
+
62
+ Set `CANON_NON_INTERACTIVE=1` to refuse every prompt.
63
+
64
+ ```bash
65
+ CANON_NON_INTERACTIVE=1 canon init --stack node --skip wiki /path/to/project
66
+ CANON_NON_INTERACTIVE=1 canon sync /path/to/project
67
+ CANON_NON_INTERACTIVE=1 canon gov install node --add astro,shadcn /path/to/project
68
+ CANON_NON_INTERACTIVE=1 canon gov sync /path/to/project
69
+ CANON_NON_INTERACTIVE=1 canon gov build /path/to/project
70
+ CANON_NON_INTERACTIVE=1 canon tooling diff astro /path/to/project
71
+ CANON_NON_INTERACTIVE=1 canon tooling sync astro /path/to/project --write
72
+ CANON_NON_INTERACTIVE=1 canon wiki init /path/to/project
73
+ canon tooling inject base /path/to/project --configs --seeds
74
+ canon tooling prune-gitignore base /path/to/project
75
+ canon tooling verify vite-react
76
+ canon audits run --json
77
+ canon secrets scan --json
78
+ canon deps audit --json
79
+ ```
69
80
 
70
- Diagnostics reach stderr in every mode, including `--json`. Name the file and the field that failed, because a JSON record carries an action and a reason and an operator reading stderr alone sees neither.
81
+ - `canon tooling sync` needs `--check` or `--write` headlessly. With neither it reports and exits 1.
82
+ - `--skip base` syncs a monorepo subtree without the layer the root owns
83
+ - `canon audits run` exits 2 on a fact and 3 for an audit that did not report
84
+ - `secrets scan` and `deps audit` exit 2 on findings. Only a credential fails `audits run`, and an advisory reports without moving the verdict.
85
+ - Both refuse with a `reason` when they have no corpus, `no-manifest` for the scan and `no-lockfile` for the audit
@@ -1,26 +1,50 @@
1
1
  ---
2
2
  title: Overview
3
- description: What this folder covers, the invocation rules every command inherits, and where domain behavior is documented instead
3
+ description: What this folder covers, the invocation rules every command inherits, and how canon docs resolves a topic name
4
4
  ---
5
5
 
6
6
  # Overview
7
7
 
8
- CLI catalog and invocation rules for an agent driving the `canon` CLI, in this repository or in any project that installed it.
8
+ Reference for an agent driving the `canon` CLI, in this repository or in any project that installed it. It covers invocation and output contracts, not domain behavior. Read the project's own `CLAUDE.md` for that.
9
9
 
10
- This folder is an index of what an agent can run and how to run it cleanly from a script. It does not cover domain behavior. Read the project's own `CLAUDE.md` for that. The `.claude/skills/internal-*` skills carry the per-domain editing guidance and live in the toolkit checkout alone, so a reader who installed the package resolves none of them.
10
+ `canon --help` is the command catalog. `canon <command> --help` lists subcommands, and `canon <command> <subcommand> --help` prints a leaf's flags, exit codes, and examples. `--help --all` prints both. A pipe gets the same text without the frame glyphs.
11
11
 
12
12
  ## Invocation rules
13
13
 
14
- `output-shape.md` holds the stream and exit contract. It applies to every command in this folder.
14
+ `output-shape.md` holds the stream and exit contract every command follows. Every command has a non-interactive path, and data on stdout pipes clean.
15
15
 
16
- ## Where to start
16
+ ## Reading a page by name
17
17
 
18
- - `output-shape.md`: the stream contract every command renders into and the exit discipline behind it, which is what a caller parsing stdout depends on
19
- - `commands.md`: the full command catalog, project-level and per-domain
20
- - `scripting.md`: the runtime catalogs that replace hardcoded names, plus headless invocation examples
18
+ `canon docs list [--json]` lists the consumer docs, plus the per-domain context entries when the install carries `canon/context/`. A registry install carries `docs/` alone. A clone or a linked worktree carries both.
21
19
 
22
- ## Related
20
+ `canon docs <topic>` prints one doc to stdout, and an unknown topic names the available ones on stderr and exits 1. Three spellings are tried in order, each across both roots before the next, with `docs/` ahead of `canon/context/`.
23
21
 
24
- - `CLAUDE.md`: project behaviors
25
- - `.claude/skills/internal-*`: domain-scoped guidance for editing work, in the toolkit checkout only
26
- - `docs/index.md`: full docs directory
22
+ - A sibling file, such as `docs/agents/indexes.md` for `indexes`
23
+ - A folder of that name, which prints its generated `index.md`
24
+ - A sub-area file one level down, such as `operating-model` reaching `docs/workflow/operating-model.md`
25
+
26
+ A bare name carried by two folders of one root resolves to neither and exits 1. Toolkit-internal topics resolve by name and stay off the list.
27
+
28
+ ## Commands with no page
29
+
30
+ `canon serve [dir]` binds loopback and prints the link that opens the directory.
31
+
32
+ - Read `url` off `--json`, since a busy port walks forward to the next free one
33
+ - `--entry` escaping the root refuses as `no-entry` before a port is taken
34
+ - Any other bind failure refuses as `bind-failed` carrying the error code
35
+ - `entryExists` reads true with no `--entry` and no root `index.html`
36
+
37
+ `canon inventory [subject]` reads `inventory.toml` at the project root and never gates. A missing server and an unmatched query refuse rather than list nothing.
38
+
39
+ ```toml
40
+ base-url = "http://localhost:4173"
41
+ routes = ["/", "/pricing"]
42
+
43
+ [subjects.focus]
44
+ query = "button, a[href], input"
45
+ ```
46
+
47
+ ## See also
48
+
49
+ - `output-shape.md`: the frame, `--json`, color, and exit discipline
50
+ - `docs/index.md`: the full docs directory
@@ -1,103 +1,61 @@
1
1
  ---
2
2
  title: Records
3
- description: The two roots a record folder resolves at, claiming the ordinal intake and groundwork share, reading each folder's size and growth, pruning stale scratch, and where validation, the canonical review count, and the private-remote backup are described
3
+ description: Backing the record folders to a private remote, restoring them on a new machine, reading folder size, and pruning stale scratch
4
+ kind: reference
4
5
  ---
5
6
 
6
7
  # Records
7
8
 
8
- ## Record roots
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 `.canon/`. The scratch folder is the one name that differs by root, spelled `.canon/tmp` and `.claude/.tmp`, since inside a dotted root the leading dot hides nothing already hidden. <!-- canon-keep-record-root -->
11
-
12
- The read order and the creation default agree. While the record folders were moving to a root of their own, creation stayed at `.claude/` so no record landed under a root whose ignore line had yet to reach a project. The ignore line ships now, so a fresh project scaffolds `.canon/` alone, and `.claude/` stays as a read fallback for a project the move has not reached.
13
-
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
-
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
-
18
- ## Validate
19
-
20
- `canon records validate <kind>` reports where a record folder disagrees with the standard governing it. Its flags, the per-kind checks, and the refusals are in `records-validate.md`.
21
-
22
- ## Ordinal
23
-
24
- `canon records ordinal <kind> <slug>` reports the next ordinal `intake` and `groundwork` share, or claims it with `--claim`. The two kinds share one sequence, per `claude/skills/plan-intake/references/intake.md` and `claude/skills/plan-groundwork/references/groundwork.md`, so this reads both `.canon/intake/` and `.canon/groundwork/` regardless of which kind was asked for.
9
+ `canon records` backs the record folders under `.canon/` to a private remote and reports on them. `canon hooks post-merge` runs `push` after every merge.
25
10
 
26
11
  ```bash
27
- canon records ordinal intake my-topic
28
- canon records ordinal groundwork my-topic --claim
29
- canon records ordinal groundwork my-topic --claim --json
30
- ```
31
-
32
- | Option | Behavior |
33
- | --------------- | ------------------------------------------------------ |
34
- | `--json` | Add a machine-readable record on stdout |
35
- | `--claim` | Create the folder atomically instead of only reporting |
36
- | `--root <path>` | Project root, defaulting to the main worktree |
37
-
38
- Without `--claim` this only reports, so two sessions reading at once can still report the same number, which is what let two sessions open two different record folders under one ordinal on the same day. `--claim` closes that: it reserves the number at a path both an `intake` claim and a `groundwork` claim resolve to identically, whichever kind is asking, and only creates the kind's own `<nn>-<slug>/` folder once that reservation is won. A losing reservation is retried against a freshly read ordinal rather than reported as a collision, bounded to five attempts before refusing as `ordinal-contended`.
39
-
40
- Exit codes: `0` reported the next ordinal, or `--claim` created the folder. `1` refused, `unknown-kind` when the argument names neither `intake` nor `groundwork`. `2` `--claim` lost every retry to a collision.
41
-
42
- ## Size
43
-
44
- `canon records size` reports what each record folder holds and how much of it is recent. It reads the same backed folders push carries, resolved the same way, plus the scratch folder, and it gates nothing.
45
-
46
- ```bash
47
- canon records size
12
+ canon records push
13
+ canon records pull
48
14
  canon records size --json
15
+ canon records prune-tmp --older-than 30
49
16
  ```
50
17
 
51
- | Option | Behavior |
52
- | --------------- | --------------------------------------------- |
53
- | `--json` | Add a machine-readable record on stdout |
54
- | `--root <path>` | Project root, defaulting to the main worktree |
55
-
56
- The table carries one row per folder that exists, heaviest first, with the file count, the bytes, a count for each growth window, and the dates of the least and most recently written file. Those dates render in the machine's local time, which is the calendar day whoever wrote the file was living in, and the reading is per-machine already. Folders that do not exist are named on one line below it rather than printed as rows of zeros. At the legacy `.claude` root, the record a `--json` call emits carries every folder in the fixed list either way, each with a `present` flag, so a caller reading the record gets a stable set of keys and can tell an absent folder from one the reading skipped. At a `.canon` root, the folder set is read off the directory itself rather than off a fixed list, so an absent folder is not listed at all: nothing enumerates a name nobody has created yet.
57
-
58
- Ordering by weight is what makes the reading worth taking. A folder listed alphabetically hides behind its neighbors, and the row a reader came for is the one that grew.
59
-
60
- The reading carries two windows rather than one, at 7 and 30 days. A single window cannot separate a folder growing steadily from one that took a single batch: a folder whose 7-day count is most of its 30-day count moved in one pass, and one where the two are proportional is growing at a rate.
18
+ Run `canon records <verb> --help` for every flag.
61
19
 
62
- 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. A pen went from 44 entries to 236 between two readings taken by hand two weeks apart, which is the measurement this replaces.
20
+ ## Output
63
21
 
64
- 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 a retired entry 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.
65
-
66
- 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.
67
-
68
- Exit codes: `0` the reading completed, `1` refused. The one refusal is `no-folder`, raised when the project holds neither record root. A project holding a root and no records is empty rather than absent: at the legacy `.claude` root each folder's own `present` flag says which ones it carries, and at a `.canon` root the folder list itself is already the answer, since nothing absent is named.
69
-
70
- ## Prune
71
-
72
- `canon records prune-tmp` reports scratch nobody has touched inside an age window, and deletes it only with `--write`. Nothing removed an abandoned scratch folder before this, so a spike folder from months ago sat beside the ones a session still needs.
22
+ Set the history up once per machine, pointed at a private repository that is not a remote of the project.
73
23
 
74
24
  ```bash
75
- canon records prune-tmp
76
- canon records prune-tmp --write
77
- canon records prune-tmp --older-than 30 --json
25
+ git --git-dir=.canon/.records.git init
26
+ git --git-dir=.canon/.records.git remote add origin <private-repo-url>
27
+ printf '.canon/\n' >> .gitignore
78
28
  ```
79
29
 
80
- | Option | Behavior |
81
- | --------------------- | -------------------------------------------------------------- |
82
- | `--json` | Add a machine-readable record on stdout |
83
- | `--write` | Delete every candidate the report lists |
84
- | `--older-than <days>` | Age a unit's newest file must clear to be offered (default 14) |
85
- | `--root <path>` | Project root, defaulting to the main worktree |
86
-
87
- A candidate is a unit whose newest file is older than the threshold: a `tmp/<slug>/` folder, a folder one level inside `runs/`, `render/`, or `pr/`, or a single marker file inside `hooks/<hook>/`. `pr/review/` groups its body files by pull request number rather than reporting as one folder, since a review pass leaves one file per pass and a folder holding thousands of them would otherwise report as a single row nobody can prune apart. A unit holding no files, empty subfolders included, is offered whatever its age.
88
-
89
- `tmp/handoff/` and `tmp/pr/poll/` are never offered. A reader deletes a handoff themselves once it has been read, and a poll baseline is live state rather than scratch. The pre-split names the reserved split replaced, `memory-routing/`, `teach-promotion/`, `ui-checklist/`, and `pr-poll/` at the scratch root, are skipped the same way on a project the rename never reached, naming the folder they moved to, rather than being offered as ordinary slugs. An unread handoff is the one thing a wrong delete here loses for good.
30
+ - `push` commits every top-level folder under `.canon/` except `tmp`, `ordinal-locks`, and `.records.git`, then pushes. New folders list under `added`.
31
+ - `pull` restores the folders on a new machine after the setup above. It refuses rather than overwrite records the history lacks.
32
+ - `size` reports one row per folder, heaviest first, with file count, bytes, and growth over 7 and 30 days. After a `pull` every file reads as new.
33
+ - `prune-tmp` lists scratch untouched for 14 days and deletes it only with `--write`. It never offers `tmp/handoff/` or `tmp/pr/poll/`.
90
34
 
91
- A scratch-root name `canon migrate record-layout` or `canon migrate scratch-evidence` moves out of scratch for good, such as `memory-archive`, is skipped the same way on a project that has not yet run that migration, naming the verb to run. The retired-entry archive `memory-archive` holds is never deleted, per the memory standard, so it stays skipped rather than aging into an offer.
35
+ `push` blocks a file over 25 MB or one `canon secrets scan` would report, listing it under `blocked`. Move it out of the record folders, then push again.
92
36
 
93
- It writes nothing until `--write` is passed, since a session record has no history to undo a wrong delete from. It reads `mtime` the way `canon records size` does, so a machine restored by `canon records pull` reads its whole tree as new and fails safe by offering nothing.
37
+ Every verb exits `0` when done and `1` when refused. `prune-tmp` exits `2` when candidates exist and `--write` was not passed.
94
38
 
95
- Exit codes: `0` nothing to prune, or `--write` deleted every candidate. `1` refused, sharing `no-folder` with `size`, or a delete failed. `2` candidates exist and `--write` was not passed.
39
+ ## Refusals
96
40
 
97
- ## Stale
41
+ Branch on `reason` in the `--json` record, not the exit code.
98
42
 
99
- `canon records stale canonical` reports when the requirements record was last reviewed and how many releases have shipped since. How the release count is taken and the refusals are in `records-stale.md`.
43
+ | Reason | Meaning |
44
+ | ------------------- | ------------------------------------------------------------------------------ |
45
+ | `no-folder` | The project holds neither record root, from `size` or `prune-tmp` |
46
+ | `split-roots` | Record folders sit under both roots. Finish the move or put them back together |
47
+ | `no-repository` | No `.canon/.records.git`. Run the setup above |
48
+ | `no-remote` | The records history has no `origin` |
49
+ | `remote-unreadable` | The project's own remotes could not be read |
50
+ | `remote-shared` | The records origin is also a remote of the project. Use a private repository |
51
+ | `no-remote-records` | `pull` found no branch on the records origin |
52
+ | `local-changes` | `pull` found records on disk that the history lacks. Run `push` first |
53
+ | `local-ahead` | `pull` found local commits that never reached the origin. Run `push` first |
54
+ | `unsafe-payload` | `push` found a file over 25 MB or carrying a credential |
55
+ | `git-failed` | A git call failed, with its stderr in the message |
100
56
 
101
- ## Push and pull
57
+ ## See also
102
58
 
103
- `canon records push` backs the record folders to a private remote and `canon records pull` restores them. The payload, the setup, and the refusals are in `records-push.md`.
59
+ - `canon records ordinal <kind> <slug> --claim`: reserve the next intake or groundwork ordinal
60
+ - `canon records validate <kind> --help`: where a record folder disagrees with its standard
61
+ - `canon records stale --help`: when the requirements record was last reviewed
@@ -1,93 +1,49 @@
1
1
  ---
2
2
  title: Sessions transfer
3
- description: Moving a session to another machine, the export bundle and its manifest, placing it with import, the refusal reasons, and what a resume on the target depends on
3
+ description: Moving a session to another machine with export and import, the manifest fields, the refusal reasons, and what a resume on the target depends on
4
+ kind: reference
4
5
  ---
5
6
 
6
7
  # Sessions transfer
7
8
 
8
- The roster these verbs check an id against is in `sessions.md`.
9
-
10
- ## Export
11
-
12
- `canon sessions export <id>` packs one session into a single gzip tarball so it can resume on another machine. The bundle holds the transcript, its side folder with every path kept, and a manifest.
9
+ `canon sessions export` packs one session into a gzip tarball, and `canon sessions import` places it under this machine's configuration directory so `claude` can resume it.
13
10
 
14
11
  ```bash
15
- canon sessions export <id>
16
12
  canon sessions export <id> --out ~/session.tar.gz --json
17
- ```
18
-
19
- | Option | Behavior |
20
- | -------------- | ---------------------------------------------------------- |
21
- | `--out <path>` | Write the bundle here rather than under the scratch folder |
22
- | `--json` | Add a machine-readable record on stdout |
23
-
24
- Without `--out` the bundle lands at `.canon/tmp/session-export/<id>.tar.gz` under the main worktree root, which is gitignored and reclaimed by `canon records prune-tmp`. It writes locally and never uploads. A transcript holds whatever tool output passed through the session, credentials included where one was printed, so treat the bundle as private.
25
-
26
- Exit codes: `0` the bundle was written, `1` refused. The refusal carries a `reason` of `not-found`, where no project folder holds a transcript for the id, or `no-archive`, where the running Bun predates `Bun.Archive`.
27
-
28
- The lookup walks every folder under the configuration directory's `projects/`, so a transcript filed under any encoding is found. The transcript is read up to its last newline and never rewritten, so a session exporting itself mid-append drops at most the half line it was writing.
29
-
30
- Export runs no skill. Write a handoff with `canon:session-compact` and push it with `canon records push` first where one is wanted. The manifest reports whether a handoff exists and whether it reached the records history, and whatever calls the verb owns that order.
31
-
32
- ### The manifest
33
-
34
- | Field | Holds |
35
- | ------------------ | ---------------------------------------------------------------------------- |
36
- | `format` | The bundle format version, `1` |
37
- | `sessionId` | The exported id |
38
- | `title` | The last custom title the transcript recorded, or `null` |
39
- | `cwd` | The last working directory the transcript recorded, or `null` |
40
- | `root` | The main worktree root of the repository the export read |
41
- | `origin` | The `origin` remote reduced to `host/path`, or `null` |
42
- | `branch`, `head` | The branch and commit of the checkout the export ran in |
43
- | `recordsHead` | The records history `HEAD`, or `null` where no records history exists |
44
- | `compacted` | Whether the transcript carries a compaction boundary |
45
- | `live` | Whether the id was live in the roster at export |
46
- | `handoff` | The newest `.canon/compact/` note written since the session began, or `null` |
47
- | `handoffCommitted` | Whether that note is committed in the records history |
48
- | `recommend` | `handoff` where a handoff exists, `transcript` otherwise |
49
-
50
- `recommend` prefers the handoff even though the transcript resumes in full. A resumed transcript carries the source machine's absolute paths into a session working somewhere else, and a handoff states the work without them.
51
-
52
- ## Import
53
-
54
- `canon sessions import <bundle>` places an exported session under this machine's configuration directory so `claude` can resume it.
55
-
56
- ```bash
57
- canon sessions import session.tar.gz
58
13
  canon sessions import session.tar.gz --root ~/repos/canon --json
59
14
  ```
60
15
 
61
- | Option | Behavior |
62
- | --------------- | ---------------------------------------------------------- |
63
- | `--root <path>` | The repository to resume in. Defaults to the main worktree |
64
- | `--json` | Add a machine-readable record on stdout |
16
+ Run `canon sessions export --help` and `canon sessions import --help` for every flag.
65
17
 
66
- The transcript and side folder land under `projects/<encoding>/`, where the encoding is the root with every character outside `[A-Za-z0-9]` turned into a dash. Import writes new files only and never overwrites.
18
+ ## Output
67
19
 
68
- Exit codes: `0` the session was placed, `1` refused. The refusal carries one of these reasons:
20
+ Export writes the transcript, its side folder, and a manifest to `.canon/tmp/session-export/<id>.tar.gz` unless `--out` says otherwise. It never uploads. A transcript can hold credentials printed in the session, so treat the bundle as private.
69
21
 
70
- | Reason | Meaning |
71
- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
72
- | `not-a-bundle` | the file is no gzip tarball, carries no manifest at format `1`, or names a path outside its own session, such as one climbing out with `..` or an absolute one |
73
- | `other-repository` | the local `origin` differs from the one the bundle recorded |
74
- | `exists` | the target transcript or side folder is already there, or any other project folder already holds a transcript for the id, which `--resume <id>` could reach first |
75
- | `live-here` | the id is live in the local roster, so a second copy would fork it |
76
- | `write-failed` | a write failed partway for a reason other than a copy appearing, such as a bundle carrying a file where it also needs a folder |
77
- | `no-archive` | the running Bun predates `Bun.Archive` |
22
+ The manifest names the session, its `branch` and `head`, and these fields:
78
23
 
79
- A write that fails partway removes every file this run placed and every folder it created before refusing, so a retry of the same bundle does not refuse on its own leftovers. Any path it could not remove is named in the message. A copy that appears between the existence check and the write refuses as `exists` on the same terms.
24
+ - `live`: whether the id was live in the roster at export
25
+ - `handoff` and `handoffCommitted`: the newest `.canon/compact/` note since the session began, and whether the records history holds it
26
+ - `recommend`: `handoff` where one exists, `transcript` otherwise, since a transcript carries the source machine's absolute paths
80
27
 
81
- A placed session can still lag the source. The record's `behind` list carries `repository-behind` when this checkout lacks the exported `HEAD`, and `records-behind` when the records history lacks the exported records commit, which `canon records pull` brings. Neither refuses, since each has a remedy the reader runs before resuming.
28
+ Import writes new files only. The record's `behind` list carries `repository-behind` when this checkout lacks the exported `HEAD`, and `records-behind` when `canon records pull` has records to bring. Neither refuses. Its two `routes` are `claude --resume <id>` and a fresh `claude` running `/canon:session-resume`.
82
29
 
83
- On success the record carries two `routes`. One runs `claude --resume <id>` from the root, and the other starts `claude` there and runs `/canon:session-resume` bare against the handoff.
30
+ Resuming while the source keeps running forks the conversation, and only `live` warns of it.
84
31
 
85
- ### What resume depends on
32
+ ## Refusals
86
33
 
87
- Measured on `claude` 2.1.281 on 2026-09-24, `--resume <id>` found a copied transcript in any folder under `projects/` and chained the next turn onto its full history, run from a repository path the transcript never recorded. `--continue` found it only under the local encoding, which is why import derives the folder rather than keeping the source machine's.
34
+ Branch on `reason` in the record, not the exit code.
88
35
 
89
- The encoding is inferred from observed folders rather than read from a published rule, and how a client shortens a path past its length limit was never observed. A client changing the rule leaves `--continue` looking elsewhere while `--resume <id>` still works. The framed report names the derived folder so a reader can compare.
36
+ | Reason | Meaning |
37
+ | ------------------ | ------------------------------------------------------------------------------------- |
38
+ | `not-found` | Export found no transcript for the id |
39
+ | `not-a-bundle` | No gzip tarball, no manifest at format `1`, or a path outside itself |
40
+ | `other-repository` | The local `origin` differs from the one the bundle recorded |
41
+ | `exists` | The transcript or side folder is already here, or another project folder holds the id |
42
+ | `live-here` | The id is live in the local roster, so a second copy would fork it |
43
+ | `write-failed` | A write failed partway. The run removes what it placed before refusing |
44
+ | `no-archive` | The running Bun predates `Bun.Archive` |
90
45
 
91
- The side folder did not change the conversation sent. What it carries is `tool-results/`, which a `persisted-output` pointer in the transcript names by the source machine's absolute path, and `subagents/`, which holds background agent transcripts. On a machine whose path matches, those pointers resolve. On one whose path differs, they dangle.
46
+ ## See also
92
47
 
93
- Resuming on the target while the source session keeps running forks the conversation, and nothing detects that across machines. The manifest's `live` field and the printed routes are the only warning.
48
+ - `canon sessions list --help`: the roster these verbs check an id against
49
+ - `canon:session-compact` and [records push](records.md): write and back up a handoff before export