@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
@@ -1,6 +1,7 @@
1
1
  import { existsSync, statSync } from 'node:fs'
2
2
  import { basename, join } from 'node:path'
3
3
  import { SUBDIRS } from '@/claude/seeds'
4
+ import { creationRel, isRecordEntry } from '@/record-root'
4
5
  import type { StampDomain } from '@/sync/stamp'
5
6
 
6
7
  const CLAUDE_DIR = '.claude'
@@ -52,7 +53,7 @@ export interface UnmigratedDomain {
52
53
  /**
53
54
  * Pairs each seed subdirectory against an uppercase-stem sibling in the target,
54
55
  * so a project still holding `.claude/TASKS.md` is reported against the
55
- * `.claude/tasks/` folder that replaced it.
56
+ * `.canon/tasks/` folder that replaced it.
56
57
  *
57
58
  * Deriving from the seed tree rather than from a fixed list means a folder
58
59
  * added later is covered without editing this file. The cost is that only an
@@ -66,7 +67,15 @@ export function collectSuperseded(target: string): SupersededEntry[] {
66
67
  const rel = join(CLAUDE_DIR, `${subdir.toUpperCase()}.md`)
67
68
  if (!isFile(join(target, rel))) continue
68
69
 
69
- entries.push({ rel, replacedBy: join(CLAUDE_DIR, subdir) })
70
+ // A record subdir now lives under the record root, so the replacement this
71
+ // names is resolved against the target rather than fixed at `.claude/`.
72
+ // Naming a folder the target does not have sends a person to migrate their
73
+ // legacy file into a path nothing reads.
74
+ const replacedBy = isRecordEntry(subdir)
75
+ ? creationRel(target, subdir)
76
+ : join(CLAUDE_DIR, subdir)
77
+
78
+ entries.push({ rel, replacedBy })
70
79
  }
71
80
 
72
81
  return entries
@@ -56,7 +56,7 @@ export type AnswersOutcome = PlanAnswers | AnswersRefused
56
56
  * project root and against the board directory both.
57
57
  *
58
58
  * The second base is the one a dispatcher actually has to hand. A board row
59
- * writes its `Plan:` link relative to `.claude/tasks/`, so the href reads
59
+ * writes its `Plan:` link relative to `.canon/tasks/`, so the href reads
60
60
  * `../plans/feature-<slug>.md`, and resolving that against the root alone lands
61
61
  * a directory above the repository and refuses a plan that exists.
62
62
  *
@@ -184,7 +184,7 @@ function isRowFor(line: string, target: string): boolean {
184
184
  /**
185
185
  * Resolves the `Plan:` target against the board and against the project root
186
186
  * both, which is how `claude-docs` reads the same line. It accepts `../plans/x.md`
187
- * and `.claude/plans/x.md` as one file, so a gate reading only the first form
187
+ * and `.canon/plans/x.md` as one file, so a gate reading only the first form
188
188
  * would pass the second and strand the plan this exists to protect.
189
189
  *
190
190
  * The resolved path is returned rather than a boolean, because the citation
@@ -213,11 +213,28 @@ export function resolveLivePlan(
213
213
  const fromBoard = resolve(dir, target)
214
214
  const fromRoot = resolve(root, target)
215
215
 
216
- if (live(fromBoard)) return fromBoard
217
- if (live(fromRoot)) return fromRoot
216
+ if (live(fromBoard)) return atOneRoot(fromBoard, plans, root)
217
+ if (live(fromRoot)) return atOneRoot(fromRoot, plans, root)
218
218
  return undefined
219
219
  }
220
220
 
221
+ /**
222
+ * Restates a live plan path at the root this tree actually resolves, so two
223
+ * tasks naming one plan through different roots compare equal.
224
+ *
225
+ * Callers compare the return of `resolveLivePlan` by string. Both roots are
226
+ * accepted above, so without this a task citing `.canon/plans/x.md` and one
227
+ * citing `.canon/plans/x.md` name one file and read as two, which counts a
228
+ * sibling's citation as absent and refuses the archive it should allow.
229
+ */
230
+ function atOneRoot(path: string, plans: string[], root: string): string {
231
+ const at = plans.find((dir) => isUnder(path, dir))
232
+
233
+ return at === undefined
234
+ ? path
235
+ : join(recordDir(root, PLANS), relative(at, path))
236
+ }
237
+
221
238
  /**
222
239
  * Names the other live tasks whose `Plan:` line lands on the same file. This is
223
240
  * the rule `claude-docs` applies before it archives a plan, held here so one
@@ -1,17 +1,17 @@
1
1
  ---
2
2
  title: Diagram reference
3
- description: Shape and content rules for .claude/diagrams/<kind>.md files
3
+ description: Shape and content rules for .canon/diagrams/<kind>.md files
4
4
  ---
5
5
 
6
6
  # Diagram reference
7
7
 
8
- Applies to per-kind entries under `.claude/diagrams/`. Skip for `index.md`, which is regenerated by `canon indexes regen`.
8
+ Applies to per-kind entries under `.canon/diagrams/`. Skip for `index.md`, which is regenerated by `canon indexes regen`.
9
9
 
10
10
  A diagram entry answers one question about the system with one or more Mermaid diagrams and the prose that makes them readable. It is not a rendering of the file tree. The check for any single line: does it tell a reader something the code layout would not have told them? If not, it belongs in `.claude/context/`.
11
11
 
12
12
  ## Scope
13
13
 
14
- Governs per-kind diagram entries under `.claude/diagrams/`: which question each answers, the Mermaid source, the accessibility fields, and the explanation prose beneath. It states the voice for that prose, which is the yield the `write-human` skill grants a surface whose own standard sets one.
14
+ Governs per-kind diagram entries under `.canon/diagrams/`: which question each answers, the Mermaid source, the accessibility fields, and the explanation prose beneath. It states the voice for that prose, which is the yield the `write-human` skill grants a surface whose own standard sets one.
15
15
 
16
16
  Does not govern:
17
17
 
@@ -39,7 +39,7 @@ An entry that fails these is non-conforming regardless of whether it satisfies e
39
39
  - `verified` (required): the short commit SHA an entry was last checked against and the ISO date of that check, separated by a space (`73e9a3f8 2026-08-02`). A stub nobody has drawn yet carries the literal `TODO: never verified` instead, which is the one other accepted value.
40
40
  - `stale` (optional): one line naming what changed under the entry since that check. Nothing writes it on its own, so it sits on an entry because a reader put it there and is absent everywhere else.
41
41
 
42
- The first three feed `.claude/diagrams/index.md` when regenerated. The catalog sorts categories alphabetically rather than in narrative order, so an entry cannot rely on its position to introduce another. Each entry names its own starting point, and the catalog's subtitle routes a first-time reader to the system context entry.
42
+ The first three feed `.canon/diagrams/index.md` when regenerated. The catalog sorts categories alphabetically rather than in narrative order, so an entry cannot rely on its position to introduce another. Each entry names its own starting point, and the catalog's subtitle routes a first-time reader to the system context entry.
43
43
 
44
44
  The marker fields reach the catalog through neither route. `canon indexes regen` reads `title`, `description`, and `category` and ignores every other key, so a marker changes no generated file. A reader picks it up by opening the entry, which is where it sits above the diagram and where anyone deciding whether to trust the picture is already standing.
45
45
 
@@ -9,7 +9,7 @@ Applies to a glossary, the file holding one entry per term a body of material de
9
9
 
10
10
  ## Scope
11
11
 
12
- Governs a glossary at `.claude/teach/<nn>-<topic>/GLOSSARY.md` and at whatever path a surface fixes for one it holds: its frontmatter, entry shape, ordering, grouping, and the rules deciding which terms it carries.
12
+ Governs a glossary at `.canon/teach/<nn>-<topic>/GLOSSARY.md` and at whatever path a surface fixes for one it holds: its frontmatter, entry shape, ordering, grouping, and the rules deciding which terms it carries.
13
13
 
14
14
  Does not govern:
15
15
 
@@ -5,13 +5,13 @@ description: Folder layout, ordinal naming, reserved numbering, frontmatter and
5
5
 
6
6
  # Groundwork reference
7
7
 
8
- Applies to a groundwork track at `.claude/groundwork/<nn>-<slug>/`. A track measures one question that has to be settled before anyone can plan against it. The numbering inside the folder is the table of contents, so a reader opens the folder and knows where to start and what follows without an index maintained inside each file.
8
+ Applies to a groundwork track at `.canon/groundwork/<nn>-<slug>/`. A track measures one question that has to be settled before anyone can plan against it. The numbering inside the folder is the table of contents, so a reader opens the folder and knows where to start and what follows without an index maintained inside each file.
9
9
 
10
10
  The folder is gitignored and unbacked. No check reaches its contents and no history recovers a deleted one, so every rule here holds only while a session reads it, and the handoff file has to be self-contained.
11
11
 
12
12
  ## Scope
13
13
 
14
- Governs a groundwork track under `.claude/groundwork/<nn>-<slug>/`: folder layout, ordinal naming, reserved numbering, frontmatter and dating, what each required file holds, and the conventions a track keeps.
14
+ Governs a groundwork track under `.canon/groundwork/<nn>-<slug>/`: folder layout, ordinal naming, reserved numbering, frontmatter and dating, what each required file holds, and the conventions a track keeps.
15
15
 
16
16
  Does not govern:
17
17
 
@@ -25,7 +25,7 @@ Does not govern:
25
25
  ## Folder name
26
26
 
27
27
  - Name the folder `<nn>-<slug>`, a two-digit zero-padded ordinal followed by a kebab-case slug.
28
- - Take the ordinal from the highest one already present across both `.claude/groundwork/` and `.claude/intake/`, incremented. A listing then sorts by when each folder opened rather than alphabetically, and the count includes both kinds because the two share one creation-order line.
28
+ - Take the ordinal from the highest one already present across both `.canon/groundwork/` and `.canon/intake/`, incremented. A listing then sorts by when each folder opened rather than alphabetically, and the count includes both kinds because the two share one creation-order line.
29
29
  - With neither folder holding an entry, the first one opened takes `01`. Do not read this off the numbering inside a track, which starts at `00` on a large one and disagrees with intake's own first file.
30
30
  - Never renumber an existing folder. The ordinal is the order it opened, and a later reader cites it by that name.
31
31
 
@@ -12,7 +12,7 @@ Reference docs for consistent authoring across the toolkit and target projects.
12
12
  - [Commit reference](commit.md): Commit message format and type conventions
13
13
  - [Context entry reference](context.md): Shape and content rules for .claude/context/<domain>.md entries
14
14
  - [Design reference](design.md): Shape and content rules for .claude/DESIGN.md
15
- - [Diagram reference](diagrams.md): Shape and content rules for .claude/diagrams/<kind>.md files
15
+ - [Diagram reference](diagrams.md): Shape and content rules for .canon/diagrams/<kind>.md files
16
16
  - [Glossary reference](glossary.md): Frontmatter, entry shape, ordering, and the rules deciding which terms a glossary carries
17
17
  - [Groundwork reference](groundwork.md): Folder layout, ordinal naming, reserved numbering, frontmatter and dating, required file contents, and conventions for a measurement track
18
18
  - [Intake reference](intake.md): Folder layout, ordinal naming, reserved index number, frontmatter and dating, the item template, the answer contract, and retrieval
@@ -30,7 +30,7 @@ Reference docs for consistent authoring across the toolkit and target projects.
30
30
  - [Slug reference](slug.md): Transform from a git branch name to a slug, and the three responses to an empty result
31
31
  - [Snippet reference](snippets.md): Snippet reference and authoring conventions
32
32
  - [Standard reference](standard.md): Shape and content rules for authoring a standard
33
- - [Tasks reference](tasks.md): Folder layout, filename convention, readiness groups, and content rules for .claude/tasks/
33
+ - [Tasks reference](tasks.md): Folder layout, filename convention, readiness groups, and content rules for .canon/tasks/
34
34
  - [Teach reference](teach.md): Workspace layout, ordinal naming, frontmatter, and the mission and learning-record formats for a learning workspace
35
35
  - [Versioning reference](versioning.md): Phase label vs semver discipline across tasks, PRs, reviews, issues, commits, and tags
36
36
  - [Wireframe reference](wireframes.md): Shape and content rules for .claude/wireframes/<surface>.md files
@@ -5,13 +5,13 @@ description: Folder layout, ordinal naming, reserved index number, frontmatter a
5
5
 
6
6
  # Intake reference
7
7
 
8
- Applies to an intake folder at `.claude/intake/<nn>-<slug>/`. One folder holds one dump, filed by domain, and every finding in it is an item carrying a measured problem, one proposed fix, and a verdict.
8
+ Applies to an intake folder at `.canon/intake/<nn>-<slug>/`. One folder holds one dump, filed by domain, and every finding in it is an item carrying a measured problem, one proposed fix, and a verdict.
9
9
 
10
10
  The folder is gitignored and unbacked. No check reaches its contents, so the shape below survives only by being read.
11
11
 
12
12
  ## Scope
13
13
 
14
- Governs an intake folder under `.claude/intake/<nn>-<slug>/`: folder layout, ordinal naming, the reserved index number, frontmatter and dating, the item format, the answer contract, and retrieval.
14
+ Governs an intake folder under `.canon/intake/<nn>-<slug>/`: folder layout, ordinal naming, the reserved index number, frontmatter and dating, the item format, the answer contract, and retrieval.
15
15
 
16
16
  Does not govern:
17
17
 
@@ -25,7 +25,7 @@ Does not govern:
25
25
  ## Folder name
26
26
 
27
27
  - Name the folder `<nn>-<slug>`, a two-digit zero-padded ordinal followed by a kebab-case slug. This is the folder's own ordinal, distinct from the `NN-<domain>.md` numbering a cluster file carries inside it.
28
- - Take the ordinal from the highest one already present across both `.claude/intake/` and `.claude/groundwork/`, incremented. A listing then sorts by when each folder opened rather than alphabetically, and the count includes both kinds because the two share one creation-order line.
28
+ - Take the ordinal from the highest one already present across both `.canon/intake/` and `.canon/groundwork/`, incremented. A listing then sorts by when each folder opened rather than alphabetically, and the count includes both kinds because the two share one creation-order line.
29
29
  - With neither folder holding an entry, the first one opened takes `01`. Do not read this off the numbering inside a dump, which starts at `00` and disagrees with groundwork's own first required file.
30
30
  - Never renumber an existing folder. The ordinal is the order it opened, and a later reader cites it by that name.
31
31
 
@@ -5,13 +5,13 @@ description: Filename and type prefix, frontmatter, the body shape per type, lin
5
5
 
6
6
  # Memory reference
7
7
 
8
- Applies to a memory entry at `.claude/memory/<type>-<slug>.md`. One file holds one rule or one fact, written at the end of the session that produced it and read by a session that holds none of it. Which surface owns a given fact is settled before an entry is written at all, and that routing is project policy rather than a shape rule.
8
+ Applies to a memory entry at `.canon/memory/<type>-<slug>.md`. One file holds one rule or one fact, written at the end of the session that produced it and read by a session that holds none of it. Which surface owns a given fact is settled before an entry is written at all, and that routing is project policy rather than a shape rule.
9
9
 
10
10
  The folder is gitignored and unbacked. Nothing recovers a deleted entry, which is why the retire step below is a move rather than a cleanup.
11
11
 
12
12
  ## Scope
13
13
 
14
- Governs a memory entry under `.claude/memory/<type>-<slug>.md`: the filename and its type prefix, the frontmatter, the body shape each type carries, links between entries, and the lifecycle from the first write to retirement.
14
+ Governs a memory entry under `.canon/memory/<type>-<slug>.md`: the filename and its type prefix, the frontmatter, the body shape each type carries, links between entries, and the lifecycle from the first write to retirement.
15
15
 
16
16
  Does not govern:
17
17
 
package/standards/plan.md CHANGED
@@ -5,13 +5,13 @@ description: Filename and slug, required sections, the suggested-and-answer cont
5
5
 
6
6
  # Plan reference
7
7
 
8
- Applies to a feature plan at `.claude/plans/feature-<slug>.md`. One file holds one concern, written before implementation starts and read by whatever executes it, so it has to carry the scope without the conversation that produced it.
8
+ Applies to a feature plan at `.canon/plans/feature-<slug>.md`. One file holds one concern, written before implementation starts and read by whatever executes it, so it has to carry the scope without the conversation that produced it.
9
9
 
10
10
  The folder is gitignored and unbacked. Nothing recovers a deleted plan, which is why the archive step below is a move rather than a cleanup.
11
11
 
12
12
  ## Scope
13
13
 
14
- Governs a feature plan under `.claude/plans/feature-<slug>.md`: the filename, the required sections, what each holds, the suggested-and-answer contract, and the lifecycle from the live folder to the archive.
14
+ Governs a feature plan under `.canon/plans/feature-<slug>.md`: the filename, the required sections, what each holds, the suggested-and-answer contract, and the lifecycle from the live folder to the archive.
15
15
 
16
16
  Does not govern:
17
17
 
@@ -124,7 +124,7 @@ This contract inverts the one an intake folder keeps, where an empty slot means
124
124
  - Write the plan before implementation starts, and treat it as the scope of the run that executes it.
125
125
  - Keep every plan at one root. A plan copied into each parallel working tree forks, and the copies answer the same question differently.
126
126
  - Amend the plan in place when a decision changes mid-flight. Do not append a second passage narrating the change, which leaves a reader to work out which of two answers is current. An execution-time deviation from a suggestion is one such amendment, and the contract above fixes which line takes it.
127
- - Move the plan to `.claude/plans/archive/` when the work it describes ships. Never delete it, because the plan is where the rejected alternative is written down and nothing else records it. The archive nests inside `.claude/plans/` rather than sitting beside it as `.claude/plans-archive/`, so an older layout is a flat sibling by that name and a current one is not.
127
+ - Move the plan to `.canon/plans/archive/` when the work it describes ships. Never delete it, because the plan is where the rejected alternative is written down and nothing else records it. The archive nests inside `.canon/plans/` rather than sitting beside it as `.claude/plans-archive/`, so an older layout is a flat sibling by that name and a current one is not.
128
128
  - Write the plan in the same session that opens the task it serves. The session executing it later inherits reasoning it would otherwise re-derive.
129
129
 
130
130
  ## Anti-patterns
@@ -5,13 +5,13 @@ description: Filename and location, the sections a handoff carries, the write an
5
5
 
6
6
  # Session map reference
7
7
 
8
- Applies to `.claude/tasks/session-<slug>.md`. A session writes one before a compaction, because a compaction keeps conclusions and drops the reasoning that produced them. It is rewritten whenever the session that owns it learns something the next session would otherwise re-derive, and it is optional: a project whose sessions never approach a compaction carries none.
8
+ Applies to `.canon/tasks/session-<slug>.md`. A session writes one before a compaction, because a compaction keeps conclusions and drops the reasoning that produced them. It is rewritten whenever the session that owns it learns something the next session would otherwise re-derive, and it is optional: a project whose sessions never approach a compaction carries none.
9
9
 
10
10
  Any session writes one. The role a session holds decides which sections it adds on top of the core, never whether it may write at all.
11
11
 
12
12
  ## Scope
13
13
 
14
- Governs the pre-compaction handoff at `.claude/tasks/session-<slug>.md`: its filename, its sections, what a writer puts in each, and how a reader picks one up.
14
+ Governs the pre-compaction handoff at `.canon/tasks/session-<slug>.md`: its filename, its sections, what a writer puts in each, and how a reader picks one up.
15
15
 
16
16
  Does not govern:
17
17
 
@@ -48,7 +48,7 @@ A standard failing these questions is non-conforming even when it satisfies ever
48
48
 
49
49
  ### Naming the file
50
50
 
51
- - Name the file for the artifact the scope statement governs, taking the word from the deepest path segment that names the artifact rather than the folder holding it. `plan.md` over `.claude/plans/feature-<slug>.md` and `session.md` over `.claude/tasks/session-<slug>.md` both follow it.
51
+ - Name the file for the artifact the scope statement governs, taking the word from the deepest path segment that names the artifact rather than the folder holding it. `plan.md` over `.canon/plans/feature-<slug>.md` and `session.md` over `.canon/tasks/session-<slug>.md` both follow it.
52
52
  - Take the singular or the plural of that word, whichever reads as the artifact. A standard over one document takes the singular, and one over a set may take either.
53
53
  - Name an attribute standard for the attribute itself, since it governs no path to derive a word from.
54
54
  - Write the derived word alone. A standard installs as a sibling and is cited by bare filename, so a qualifier makes an outlier that every later citation has to carry.
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  title: Tasks reference
3
- description: Folder layout, filename convention, readiness groups, and content rules for .claude/tasks/
3
+ description: Folder layout, filename convention, readiness groups, and content rules for .canon/tasks/
4
4
  ---
5
5
 
6
6
  # Tasks reference
7
7
 
8
- Applies to `.claude/tasks/`. Tracks what is being built and why, at the level of features and outcomes. One file per task.
8
+ Applies to `.canon/tasks/`. Tracks what is being built and why, at the level of features and outcomes. One file per task.
9
9
 
10
10
  Update when a task starts, completes, or changes scope. When to open a task at all is project policy, not a shape rule, and lives in `CLAUDE.md`.
11
11
 
@@ -13,7 +13,7 @@ The folder is gitignored. Board state changes when work ships rather than when a
13
13
 
14
14
  ## Scope
15
15
 
16
- Governs the task board under `.claude/tasks/`: folder layout, filenames, frontmatter, file format, origin lines, execution ordering, the backlog beside it, and archiving.
16
+ Governs the task board under `.canon/tasks/`: folder layout, filenames, frontmatter, file format, origin lines, execution ordering, the backlog beside it, and archiving.
17
17
 
18
18
  Does not govern:
19
19
 
@@ -26,7 +26,7 @@ Does not govern:
26
26
  ## Layout
27
27
 
28
28
  ```plaintext
29
- .claude/tasks/
29
+ .canon/tasks/
30
30
  ├── index.md ← generated, never hand-edited
31
31
  ├── priority.md ← hand-maintained execution order
32
32
  ├── backlog.md ← unordered, what is not being scheduled
@@ -205,13 +205,13 @@ A task with no origin is either lost context or work nobody decided to do. The i
205
205
 
206
206
  An intake folder answers that direction at folder scope rather than item scope, since one dump dispositions many items and most close without ever becoming a task. What names a folder is every item answered and no task citing it, on the board or in the archive. That is a dump nobody acted on. Counting the archive beside the board is what separates it from one already promoted and shipped, and a check reading the board alone calls every finished folder abandoned.
207
207
 
208
- `Plan:`, `Groundwork:`, and `Intake:` name their target as a markdown link whose text is the file or folder stem, so the line resolves on a ctrl-click the way `priority.md` rows already do. Write the path relative to `.claude/tasks/`, which makes it `../plans/`, `../groundwork/`, and `../intake/`. A path written from the project root renders as a link and resolves to nothing in an editor rooted at the project. `Issue:` stays a bare `#NNN`, since an issue number is not a path and a full URL would write the remote into a gitignored file.
208
+ `Plan:`, `Groundwork:`, and `Intake:` name their target as a markdown link whose text is the file or folder stem, so the line resolves on a ctrl-click the way `priority.md` rows already do. Write the path relative to `.canon/tasks/`, which makes it `../plans/`, `../groundwork/`, and `../intake/`. A path written from the project root renders as a link and resolves to nothing in an editor rooted at the project. `Issue:` stays a bare `#NNN`, since an issue number is not a path and a full URL would write the remote into a gitignored file.
209
209
 
210
210
  Phase-label format and where labels may appear are governed by `standards/versioning.md`.
211
211
 
212
212
  `Plan:` points at `../plans/feature-<slug>.md` while the task is open. Once the task ships and the plan is archived, it points at `../plans/archive/feature-<slug>.md`, and at `../../plans/archive/feature-<slug>.md` once the task itself is archived a folder deeper. Retarget both halves of the link rather than dropping it, so a completed task still leads to the reasoning behind it.
213
213
 
214
- A project that archived plans before the folder nested under `.claude/plans/` holds closed tasks pointing at `../plans-archive/`, or at `../.tmp/plans-archive/` from before the durable records left the scratch tree. Each form resolves against the files it names, so leave those pointers where they are. A named route now moves the folder and retargets its pointers together, but no automation runs it, so an unmigrated project keeps holding the old spelling until someone does, and a task retargeted without its plan moving leads nowhere.
214
+ A project that archived plans before the folder nested under `.canon/plans/` holds closed tasks pointing at `../plans-archive/`, or at `../.tmp/plans-archive/` from before the durable records left the scratch tree. Each form resolves against the files it names, so leave those pointers where they are. A named route now moves the folder and retargets its pointers together, but no automation runs it, so an unmigrated project keeps holding the old spelling until someone does, and a task retargeted without its plan moving leads nowhere.
215
215
 
216
216
  One plan per task. A plan cited by two tasks is a misfile rather than a shape to design for, which is why the sweep counts citations before archiving: the count is a guard against the misfile stranding a pointer, not support for the shape.
217
217
 
@@ -241,11 +241,11 @@ The line is what lets a merge close its own task. Every merge on `main` is a squ
241
241
 
242
242
  ## Archiving
243
243
 
244
- Never delete a task file. A shipped task moves to `.claude/tasks/archive/` under its own name, and the live index regenerates without it. `canon tasks archive` owns that move, and what it does and what it refuses on are at `docs/agents/tasks.md`.
244
+ Never delete a task file. A shipped task moves to `.canon/tasks/archive/` under its own name, and the live index regenerates without it. `canon tasks archive` owns that move, and what it does and what it refuses on are at `docs/agents/tasks.md`.
245
245
 
246
- The archive nests inside `.claude/tasks/` rather than sitting beside it as a flat `.claude/task-archive/`. Nesting is what lets a reader tell the two shapes apart on sight: the flat sibling is what a binary predating this convention still writes, so meeting one names an older checkout rather than a second archive to reconcile against this one.
246
+ The archive nests inside `.canon/tasks/` rather than sitting beside it as a flat `.claude/task-archive/`. Nesting is what lets a reader tell the two shapes apart on sight: the flat sibling is what a binary predating this convention still writes, so meeting one names an older checkout rather than a second archive to reconcile against this one.
247
247
 
248
- One destination rather than a per-project choice is what lets the move happen without asking. It mirrors the plans archive at `.claude/plans/archive/`, sitting inside the folder it archives the same way, and it inherits the board's own ignore entry rather than needing one of its own. The cost is that an archived task does not appear in diffs, which is the cost the live board already carries.
248
+ One destination rather than a per-project choice is what lets the move happen without asking. It mirrors the plans archive at `.canon/plans/archive/`, sitting inside the folder it archives the same way, and it inherits the board's own ignore entry rather than needing one of its own. The cost is that an archived task does not appear in diffs, which is the cost the live board already carries.
249
249
 
250
250
  Archiving a task does not archive its plan. `claude-docs` owns the plans sweep and moves a plan only when the closing task is its last live citation, so the sweep runs before the archive rather than after it. The sweep finds its work by scanning the live folder, and a task archived first is beyond its reach for good, leaving the plan with no live task citing it and an archived task pointing at a path nothing will retarget.
251
251
 
@@ -5,13 +5,13 @@ description: Workspace layout, ordinal naming, frontmatter, and the mission and
5
5
 
6
6
  # Teach reference
7
7
 
8
- Applies to a learning workspace at `.claude/teach/<nn>-<topic>/`. One workspace holds one subject studied across sessions, and it carries both halves of what studying produces: the durable reference material a reader consults later, and the disposable lessons a learner works through once.
8
+ Applies to a learning workspace at `.canon/teach/<nn>-<topic>/`. One workspace holds one subject studied across sessions, and it carries both halves of what studying produces: the durable reference material a reader consults later, and the disposable lessons a learner works through once.
9
9
 
10
10
  The folder is gitignored. Its markdown half is written in a format the authoring gates read, so a page promoted out of it later needs no conversion.
11
11
 
12
12
  ## Scope
13
13
 
14
- Governs a learning workspace under `.claude/teach/<nn>-<topic>/`: folder layout, ordinal naming, frontmatter, and the mission and learning-record formats.
14
+ Governs a learning workspace under `.canon/teach/<nn>-<topic>/`: folder layout, ordinal naming, frontmatter, and the mission and learning-record formats.
15
15
 
16
16
  Does not govern:
17
17
 
@@ -40,7 +40,7 @@ External release identity used in git tags and release notes. Independent of pha
40
40
 
41
41
  | Surface | Phase labels | Semver tags |
42
42
  | ------------------------- | ------------ | ----------------------------------- |
43
- | `.claude/tasks/` | yes | no |
43
+ | `.canon/tasks/` | yes | no |
44
44
  | Chat with the operator | yes | no |
45
45
  | PR titles | no | only when the PR cuts a release |
46
46
  | PR bodies | no | only when the PR cuts a release |
@@ -27,7 +27,7 @@ root=$(git worktree list --porcelain 2>/dev/null | grep -m 1 '^worktree ' | cut
27
27
  # The board is shared scratch at the main worktree root, and `git worktree
28
28
  # list` puts that root first. A pull inside a linked worktree fires this same
29
29
  # hook, so trusting the working directory would read a board that is not there.
30
- board="$root/.claude/tasks"
30
+ board="$root/.canon/tasks"
31
31
  [ -d "$board" ] || exit 0
32
32
 
33
33
  closed=""
@@ -50,7 +50,7 @@ Seeds live in `tooling/base/seeds/`. Sync drops each once on first install and n
50
50
  - `pre-commit` runs `bunx lint-staged`.
51
51
  - `commit-msg` runs `bunx commitlint --edit "$1"`.
52
52
  - `pre-push` runs `bun run check`.
53
- - `post-merge` names `.claude/tasks/` archive candidates, staying silent otherwise and when the board is absent.
53
+ - `post-merge` names `.canon/tasks/` archive candidates, staying silent otherwise and when the board is absent.
54
54
  - `post-rewrite` delegates to `post-merge` on `rebase`, so a `pull.rebase=true` machine still gets the check.
55
55
 
56
56
  ## lint-staged
@@ -8,7 +8,17 @@ runtime = ""
8
8
  scaffold = ""
9
9
 
10
10
  # Checked against this repository's own `.gitignore` by
11
- # scripts/core/check-ignore-parity.sh, which also holds the two `.claude/` paths
12
- # this array deliberately omits and the reason each stays out.
11
+ # scripts/core/check-ignore-parity.sh, which carries no sanctioned divergence
12
+ # now that both sides ship the same two entries.
13
+ #
14
+ # One root entry rather than a folder apiece, so a target that gains a record
15
+ # folder later receives no manifest edit and no sync. `.claude/worktrees/` is
16
+ # the carve-out, since the harness requires a worktree to sit there.
17
+ #
18
+ # The cost is recorded rather than mitigated, and the operator took it on
19
+ # 2026-09-01 against the two alternatives. A target that syncs and never runs
20
+ # `canon migrate records` still holds its records under `.claude/`, and this
21
+ # array no longer tells it to ignore them, so the first sign is a memory file or
22
+ # a task board appearing in someone's commit.
13
23
  [gitignore]
14
- "# Claude" = [".canon/", ".claude/.records.git/", ".claude/.tmp/", ".claude/groundwork/", ".claude/intake/", ".claude/memory/", ".claude/plans/", ".claude/proposals/", ".claude/review/", ".claude/worktrees/", ".claude/tasks/", ".claude/teach/"]
24
+ "# Claude" = [".canon/", ".claude/worktrees/"]
@@ -9,49 +9,62 @@ The claude stack installs the `.claude/` workflow directory into a project. Stat
9
9
  ```plaintext
10
10
  .claude/
11
11
  ├── CLAUDE.md ← seeded. Project context and rules, auto-loaded by Claude Code each session
12
- ├── tasks/ ← seeded then gitignored. One file per task plus a generated index.md, local scratch only. `archive/` holds the tasks that shipped.
13
12
  ├── REQUIREMENTS.md ← seeded. Project goals, non-goals, MVP scope
14
13
  ├── ARCHITECTURE.md ← seeded. Technical design decisions and open questions
15
14
  ├── DESIGN.md ← seeded. Visual intent and the decisions behind it
16
15
  ├── wireframes/ ← seeded. Per-surface ASCII layouts. `index.md` is the discovery anchor; `<surface>.md` files hold the sketches and behavior bullets.
17
- ├── diagrams/ ← seeded. Per-kind Mermaid views. `index.md` is the discovery anchor. `<kind>.md` files hold one diagram each, grouped by the `category` frontmatter field.
18
16
  ├── GOV.md ← retired. Removed by `canon gov sync` if present from a prior install
19
17
  ├── settings.json ← seeded. Project-level Claude Code config (PreToolUse and PostToolUse hooks). User-level config installed separately via `canon claude setup`.
20
- ├── plans/ ← execution detail for multi-step tasks, gitignored. `feature-*.md` entries swept by claude-docs into `archive/`.
21
- ├── review/ ← gitignored, one subfolder per producer. `branch/` for claude-review, `feedback/` for canon feedback, `memory/` for claude-memory-review with its own `archive/`, `design/` for canon design render.
22
- ├── .tmp/ ← ephemeral scratch space, gitignored
23
- └── memory/ ← session facts no context entry owns, gitignored. `index.md` regenerated by a hook.
18
+ └── worktrees/ ← gitignored. Where EnterWorktree creates a linked worktree, which is why it did not move.
19
+
20
+ .canon/
21
+ ├── tasks/ ← seeded. One file per task plus a generated index.md, local scratch only. `archive/` holds the tasks that shipped.
22
+ ├── diagrams/ ← seeded. Per-kind Mermaid views. `index.md` is the discovery anchor. `<kind>.md` files hold one diagram each, grouped by the `category` frontmatter field.
23
+ ├── plans/ ← execution detail for multi-step tasks. `feature-*.md` entries swept by claude-docs into `archive/`.
24
+ ├── review/ ← one subfolder per producer. `branch/` for claude-review, `feedback/` for canon feedback, `memory/` for claude-memory-review with its own `archive/`, `design/` for canon design render.
25
+ ├── tmp/ ← ephemeral scratch space, safe to delete without loss
26
+ └── memory/ ← session facts no context entry owns. `index.md` regenerated by a hook.
24
27
  ```
25
28
 
29
+ One ignore entry covers `.canon/` whole, so nothing under it is named separately and nothing tracked may ever land there. A project scaffolded before the move keeps its records under `.claude/`, and every command reads either root.
30
+
31
+ ## Moving the records to `.canon/`
32
+
33
+ A project installed before the split still holds its records under `.claude/`. `canon migrate records` moves them and repoints every tracked file that cites one, reporting the plan until `--write` is passed.
34
+
35
+ Run `canon tooling sync claude . --write` first. The verb refuses while the project does not ignore `.canon/`, because every folder it relocates is ignored where it stands and landing one under a tracked root commits the memory pen. Until the move runs, this project's records sit at a root the shipped ignore set no longer names, so a `git add -A` in that window picks them up.
36
+
37
+ The verb ships with the change that split the roots and is not in a published release yet, so an installed binary answers `unknown command` until one carries it. Run it out of a canon checkout in the meantime, as `bun src/cli.ts migrate records --root <project> --write`, and read the `ok` field out of `--json` rather than the exit code, which a shell wrapper around `canon` can flatten.
38
+
26
39
  ## Upgrading from a single-file board
27
40
 
28
- A project installed before the board became a folder still holds `.claude/TASKS.md`. Nothing migrates it. `canon claude init` seeds an empty `.claude/tasks/index.md` beside it and leaves the old file untouched, so every skill reads the empty catalog while the real board sits unreferenced.
41
+ A project installed before the board became a folder still holds `.claude/TASKS.md`. Nothing migrates it. `canon claude init` seeds an empty `.canon/tasks/index.md` beside it and leaves the old file untouched, so every skill reads the empty catalog while the real board sits unreferenced.
29
42
 
30
43
  Convert by hand, once per project:
31
44
 
32
- 1. Split each `###` block into `.claude/tasks/vXX.Y-<slug>.md`, adding the `title` and `description` frontmatter the tasks standard requires, which `canon standards tasks` prints. Compare each new file against the block it came from rather than checking that every block landed somewhere.
33
- 2. Run `canon indexes regen --no-stage --root . .claude/tasks/<any-task>.md` to build the catalog.
34
- 3. Delete `.claude/TASKS.md`, and swap its `.gitignore` entry for `.claude/tasks/`.
45
+ 1. Split each `###` block into `.canon/tasks/vXX.Y-<slug>.md`, adding the `title` and `description` frontmatter the tasks standard requires, which `canon standards tasks` prints. Compare each new file against the block it came from rather than checking that every block landed somewhere.
46
+ 2. Run `canon indexes regen --no-stage --root . .canon/tasks/<any-task>.md` to build the catalog.
47
+ 3. Delete `.claude/TASKS.md`, and swap its `.gitignore` entry for `.canon/tasks/`.
35
48
 
36
49
  ## Upgrading a hand-appended memory index
37
50
 
38
- A project installed before the memory folder gained a generated index still holds `.claude/memory/MEMORY.md`, and its entries still carry `name` and `type` frontmatter. Nothing migrates it. `claude-memory-capture` stops appending rows once the new seed lands, so the old file freezes at whatever it held while the folder keeps growing past it.
51
+ A project installed before the memory folder gained a generated index still holds `.canon/memory/MEMORY.md`, and its entries still carry `name` and `type` frontmatter. Nothing migrates it. `claude-memory-capture` stops appending rows once the new seed lands, so the old file freezes at whatever it held while the folder keeps growing past it.
39
52
 
40
53
  Convert by hand, once per project:
41
54
 
42
55
  1. Rewrite each entry's `name` key to `title` and its `type` key to a sentence-case `category`, quoting any `description` that opens with a backtick or a colon so the frontmatter parses.
43
56
  2. Replace `MEMORY.md` with an `index.md` carrying `title` and `subtitle` frontmatter and nothing else.
44
- 3. Run `canon indexes regen --no-stage --root . .claude/memory/index.md` to build the catalog, and compare its entry count against the file count before deleting anything.
57
+ 3. Run `canon indexes regen --no-stage --root . .canon/memory/index.md` to build the catalog, and compare its entry count against the file count before deleting anything.
45
58
 
46
59
  ## Upgrading from a single-file diagram set
47
60
 
48
- A project installed before the diagram surface became a folder still holds `.claude/DIAGRAMS.md`. Unlike the board, this one migrates itself. The `claude-diagram` skill reads the flat file when `.claude/diagrams/` holds no entries, splits it by kind into the folder, and reports what it wrote. The old file stays on disk so the split can be compared against its source, and deleting it is a manual step once that check passes.
61
+ A project installed before the diagram surface became a folder still holds `.claude/DIAGRAMS.md`. Unlike the board, this one migrates itself. The `claude-diagram` skill reads the flat file when `.canon/diagrams/` holds no entries, splits it by kind into the folder, and reports what it wrote. The old file stays on disk so the split can be compared against its source, and deleting it is a manual step once that check passes.
49
62
 
50
63
  ## Gitignore
51
64
 
52
- - `# Claude`: `.claude/.records.git/`, `.claude/.tmp/`, `.claude/groundwork/`, `.claude/intake/`, `.claude/memory/`, `.claude/plans/`, `.claude/review/`, `.claude/worktrees/`, `.claude/tasks/`, `.claude/teach/`
65
+ - `# Claude`: `.canon/`, `.claude/worktrees/`
53
66
 
54
- Each entry is a top-level record folder, and an archive sits inside the folder it archives, so one line covers a record and everything it retires.
67
+ The first entry covers the whole record root, so a record folder added later needs no line here and no sync. The second is the carve-out, since the harness creates a worktree under `.claude/` and requires its target to sit there.
55
68
 
56
69
  ## CLI
57
70
 
@@ -27,19 +27,19 @@ case "$file_path" in
27
27
  esac
28
28
 
29
29
  # A project whose own root sits under a path carrying a tmp segment is not
30
- # writing to system temp, and the bare pattern below trips the guard on every
31
- # source file it holds. Anchor on the project root before the pattern match.
32
- #
33
- # This gives up one case on purpose: a write to <project>/tmp/ is a genuine
34
- # scratch violation that no longer warns. The false positive fires on every
35
- # source write in an affected project, so the trade favors the anchor.
30
+ # writing to system temp, and the bare pattern below would trip on every source
31
+ # file it holds. The project root is stripped before the match rather than
32
+ # exiting on it, so the segments the patterns look for are the ones the write
33
+ # adds. Exiting on any path under the project gives up <project>/tmp/ as well,
34
+ # which is a genuine violation this still warns on.
35
+ matched=$file_path
36
36
  if [ -n "${CLAUDE_PROJECT_DIR:-}" ]; then
37
37
  case "$file_path" in
38
- "$CLAUDE_PROJECT_DIR"/*) exit 0 ;;
38
+ "$CLAUDE_PROJECT_DIR"/*) matched=${file_path#"$CLAUDE_PROJECT_DIR"} ;;
39
39
  esac
40
40
  fi
41
41
 
42
- case "$file_path" in
42
+ case "$matched" in
43
43
  */tmp/* | *\\tmp\\* | */Temp/* | *\\Temp\\* | */var/folders/*) ;;
44
44
  *) exit 0 ;;
45
45
  esac