@erclx/canon 5.31.0 → 5.32.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 (303) hide show
  1. package/README.md +1 -1
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/references/adopted.md +1 -1
  4. package/claude/skills/auto-ship/REQUIREMENT.md +4 -16
  5. package/claude/skills/auto-ship/SKILL.md +13 -58
  6. package/claude/skills/auto-ship/references/draft-mark.md +2 -2
  7. package/claude/skills/auto-ship/references/failure-recovery.md +1 -5
  8. package/claude/skills/auto-ship/references/tier-failures.md +1 -1
  9. package/claude/skills/auto-ship/references/ui-checklist.md +2 -2
  10. package/claude/skills/auto-ship/references/verb-absent.md +3 -27
  11. package/claude/skills/backlog-triage/SKILL.md +1 -1
  12. package/claude/skills/canon-cli/REQUIREMENT.md +4 -6
  13. package/claude/skills/canon-cli/SKILL.md +2 -2
  14. package/claude/skills/canon-feedback/REQUIREMENT.md +1 -1
  15. package/claude/skills/canon-feedback/SKILL.md +10 -13
  16. package/claude/skills/canon-feedback-triage/REQUIREMENT.md +13 -14
  17. package/claude/skills/canon-feedback-triage/SKILL.md +49 -30
  18. package/claude/skills/canvas/REQUIREMENT.md +3 -4
  19. package/claude/skills/canvas/SKILL.md +5 -5
  20. package/{standards → claude/skills/canvas/references}/canvas.md +12 -1
  21. package/claude/skills/ci-workflow/REQUIREMENT.md +0 -1
  22. package/claude/skills/context-fold/REQUIREMENT.md +8 -20
  23. package/claude/skills/context-fold/SKILL.md +20 -44
  24. package/claude/skills/context-fold/references/classify.md +7 -8
  25. package/claude/skills/context-fold/references/completion-report.md +2 -6
  26. package/claude/skills/context-fold/references/context-refresh.md +5 -15
  27. package/claude/skills/context-fold/references/promoted-pages.md +3 -3
  28. package/claude/skills/context-fold/references/receipt-sweep.md +6 -17
  29. package/claude/skills/context-fold/references/unusable-baseline.md +4 -6
  30. package/claude/skills/create-rule/REQUIREMENT.md +0 -1
  31. package/claude/skills/create-skill/REQUIREMENT.md +6 -4
  32. package/claude/skills/create-skill/SKILL.md +8 -7
  33. package/{standards → claude/skills/create-skill/references}/skill-paths.md +1 -0
  34. package/claude/skills/decision-escalate/REQUIREMENT.md +1 -1
  35. package/claude/skills/decision-escalate/SKILL.md +1 -1
  36. package/claude/skills/design-taste/REQUIREMENT.md +0 -2
  37. package/claude/skills/design-taste/SKILL.md +1 -3
  38. package/claude/skills/design-taste/references/vocabulary.md +1 -1
  39. package/claude/skills/docs-sync/REQUIREMENT.md +3 -3
  40. package/claude/skills/document-health/REQUIREMENT.md +2 -2
  41. package/claude/skills/document-health/SKILL.md +4 -5
  42. package/claude/skills/document-health/references/axes.md +4 -6
  43. package/claude/skills/draft-and-pick/REQUIREMENT.md +16 -14
  44. package/claude/skills/draft-and-pick/SKILL.md +40 -32
  45. package/claude/skills/draft-and-pick/references/live-arms.md +9 -9
  46. package/claude/skills/draft-doc/REQUIREMENT.md +5 -5
  47. package/claude/skills/draft-doc/SKILL.md +9 -10
  48. package/claude/skills/draft-doc/references/requirements.md +61 -0
  49. package/claude/skills/draft-doc/references/wiki-kind.md +53 -0
  50. package/claude/skills/draft-doc/references/wiki.md +50 -30
  51. package/claude/skills/draft-figure/REQUIREMENT.md +2 -2
  52. package/claude/skills/draft-figure/SKILL.md +4 -4
  53. package/claude/skills/draft-figure/references/architecture-set.md +4 -4
  54. package/claude/skills/draft-figure/references/hand-drawn.md +1 -1
  55. package/claude/skills/draft-figure/references/mermaid-path.md +55 -0
  56. package/claude/skills/draft-figure/references/mermaid.md +43 -34
  57. package/claude/skills/draft-identity/REQUIREMENT.md +1 -1
  58. package/claude/skills/draft-identity/SKILL.md +9 -7
  59. package/claude/skills/draft-ready/REQUIREMENT.md +1 -1
  60. package/claude/skills/draft-ready/SKILL.md +6 -6
  61. package/claude/skills/draft-ready/references/assembly.md +1 -1
  62. package/{standards → claude/skills/draft-ready/references}/ready.md +13 -5
  63. package/claude/skills/git-issue/references/issue.md +3 -3
  64. package/claude/skills/git-ship/REQUIREMENT.md +0 -2
  65. package/claude/skills/git-ship/SKILL.md +16 -24
  66. package/claude/skills/plan-feature/SKILL.md +2 -6
  67. package/{standards → claude/skills/plan-feature/references}/plan.md +9 -10
  68. package/claude/skills/plan-groundwork/SKILL.md +5 -6
  69. package/{standards → claude/skills/plan-groundwork/references}/groundwork.md +17 -4
  70. package/claude/skills/plan-intake/SKILL.md +2 -3
  71. package/{standards → claude/skills/plan-intake/references}/intake.md +17 -2
  72. package/claude/skills/plan-intake-answer/REQUIREMENT.md +1 -1
  73. package/claude/skills/plan-intake-answer/SKILL.md +1 -1
  74. package/claude/skills/review-address/SKILL.md +1 -1
  75. package/claude/skills/review-craft/REQUIREMENT.md +3 -3
  76. package/claude/skills/review-craft/SKILL.md +2 -2
  77. package/claude/skills/review-craft/references/ui.md +4 -4
  78. package/claude/skills/review-pr/REQUIREMENT.md +1 -1
  79. package/claude/skills/review-pr/SKILL.md +5 -7
  80. package/claude/skills/review-ui/REQUIREMENT.md +0 -1
  81. package/claude/skills/review-ui/SKILL.md +1 -1
  82. package/claude/skills/role-orchestrator/REQUIREMENT.md +0 -3
  83. package/claude/skills/role-orchestrator/SKILL.md +8 -8
  84. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +3 -3
  85. package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +7 -10
  86. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
  87. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +0 -3
  88. package/claude/skills/role-orchestrator/references/orchestrator-resume.md +1 -1
  89. package/claude/skills/role-planner/SKILL.md +2 -2
  90. package/claude/skills/role-reviewer/SKILL.md +1 -1
  91. package/claude/skills/role-worker/SKILL.md +2 -2
  92. package/claude/skills/session-compact/REQUIREMENT.md +1 -4
  93. package/claude/skills/session-compact/SKILL.md +3 -13
  94. package/claude/skills/session-compact/references/handoff-note.md +2 -2
  95. package/claude/skills/session-map/REQUIREMENT.md +1 -9
  96. package/claude/skills/session-map/SKILL.md +8 -16
  97. package/{standards → claude/skills/session-map/references}/session.md +17 -11
  98. package/claude/skills/session-relay/REQUIREMENT.md +1 -1
  99. package/claude/skills/session-resume/REQUIREMENT.md +4 -6
  100. package/claude/skills/session-resume/SKILL.md +3 -9
  101. package/claude/skills/session-worktree/SKILL.md +1 -1
  102. package/claude/skills/session-worktree/references/main-root-writes.md +2 -2
  103. package/claude/skills/systematic-debugging/REQUIREMENT.md +0 -1
  104. package/claude/skills/target-check/REQUIREMENT.md +3 -3
  105. package/claude/skills/target-check/SKILL.md +1 -1
  106. package/claude/skills/target-check/references/domains.md +1 -1
  107. package/claude/skills/target-setup/REQUIREMENT.md +4 -4
  108. package/claude/skills/target-setup/SKILL.md +3 -3
  109. package/claude/skills/target-setup/references/detect.md +1 -1
  110. package/claude/skills/task-board/SKILL.md +4 -4
  111. package/claude/skills/task-board/references/archive.md +1 -1
  112. package/{standards → claude/skills/task-board/references}/board.md +9 -0
  113. package/claude/skills/task-board/references/decline.md +1 -1
  114. package/{standards → claude/skills/task-board/references}/tasks.md +13 -8
  115. package/claude/skills/teach-workspace/SKILL.md +1 -1
  116. package/claude/skills/teach-workspace/references/revisit.md +1 -1
  117. package/{standards → claude/skills/teach-workspace/references}/teach.md +17 -5
  118. package/claude/skills/ux-audit/REQUIREMENT.md +2 -2
  119. package/claude/skills/ux-audit/SKILL.md +2 -3
  120. package/claude/skills/write-human/REQUIREMENT.md +0 -1
  121. package/claude/skills/write-human/SKILL.md +1 -1
  122. package/docs/agents/audits.md +1 -1
  123. package/docs/agents/canvas.md +1 -1
  124. package/docs/agents/commands.md +1 -6
  125. package/docs/agents/context-audit-checks.md +9 -23
  126. package/docs/agents/context-audit.md +7 -15
  127. package/docs/agents/context-classify.md +5 -5
  128. package/docs/agents/counts.md +5 -5
  129. package/docs/agents/drift-surfaces.md +2 -2
  130. package/docs/agents/index.md +3 -5
  131. package/docs/agents/install-and-sync.md +6 -6
  132. package/docs/agents/intake.md +1 -1
  133. package/docs/agents/records-stale.md +5 -5
  134. package/docs/agents/records-validate.md +1 -1
  135. package/docs/agents/records.md +2 -2
  136. package/docs/agents/restated.md +1 -1
  137. package/docs/agents/rule-citations.md +1 -1
  138. package/docs/agents/skills-audit.md +2 -2
  139. package/docs/agents/state-scoped-risk.md +2 -2
  140. package/docs/agents/superseded.md +2 -2
  141. package/docs/agents/tasks-plans.md +1 -1
  142. package/docs/agents/tasks-validate.md +1 -1
  143. package/docs/agents/tasks.md +1 -1
  144. package/docs/agents/teach.md +1 -1
  145. package/docs/agents/upstream.md +26 -2
  146. package/docs/target/migrations.md +24 -0
  147. package/docs/target/projects.md +5 -7
  148. package/docs/target/scenarios.md +2 -2
  149. package/docs/target/sync.md +3 -3
  150. package/docs/workflow/ai-workflow.md +7 -38
  151. package/docs/workflow/index.md +1 -1
  152. package/docs/workflow/operating-model.md +8 -8
  153. package/docs/workflow/parallel-features.md +7 -7
  154. package/docs/workflow/skill-map-on-request.md +12 -15
  155. package/docs/workflow/skill-map.md +20 -32
  156. package/docs/workflow/visual-design-workflow.md +11 -14
  157. package/governance/renames.toml +0 -19
  158. package/governance/rules/canon/605-worktrees.md +1 -1
  159. package/governance/rules/{standards → canon}/plan.md +4 -5
  160. package/governance/rules/canon/tasks.md +18 -0
  161. package/governance/stacks/base.toml +1 -1
  162. package/package.json +1 -1
  163. package/scripts/core/install-check.sh +1 -1
  164. package/scripts/core/regen-web-previews.ts +1 -1
  165. package/src/audits/catalog.ts +5 -97
  166. package/src/claude/cases/authoring.ts +0 -18
  167. package/src/claude/cases/misc.ts +0 -13
  168. package/src/claude/cases/setup.ts +3 -20
  169. package/src/claude/cases/workflow.ts +0 -33
  170. package/src/claude/seeds.ts +1 -7
  171. package/src/claude/skills-audit.ts +1 -1
  172. package/src/cli/help.ts +1 -4
  173. package/src/cli.ts +0 -4
  174. package/src/commands/claude.ts +8 -14
  175. package/src/commands/context.ts +18 -280
  176. package/src/commands/feedback.ts +22 -91
  177. package/src/commands/records.ts +5 -5
  178. package/src/commands/sync.ts +5 -3
  179. package/src/commands/upstream.ts +135 -0
  180. package/src/context/audit.ts +5 -14
  181. package/src/context/classify/extract.ts +6 -13
  182. package/src/context/classify/patterns.ts +2 -41
  183. package/src/context/classify/prompts.ts +2 -6
  184. package/src/context/classify/run.ts +3 -3
  185. package/src/context/folders.ts +7 -12
  186. package/src/context/gate.ts +11 -91
  187. package/src/context/requirements.ts +13 -4
  188. package/src/counts/scan.ts +3 -3
  189. package/src/design/components.ts +2 -2
  190. package/src/feedback/body.ts +0 -12
  191. package/src/gate/measures.ts +6 -57
  192. package/src/gov/citations.ts +9 -3
  193. package/src/gov/restated.ts +3 -3
  194. package/src/markdown/structure.ts +2 -2
  195. package/src/migrate/record-layout.ts +3 -3
  196. package/src/records/ordinal.ts +1 -1
  197. package/src/records/prune.ts +2 -1
  198. package/src/records/stale-canonical.ts +2 -2
  199. package/src/roots/record.ts +1 -1
  200. package/src/sandbox/scenario.ts +115 -11
  201. package/src/shipped/references.ts +3 -3
  202. package/src/standards/read.ts +31 -0
  203. package/src/sync/seeds-report.ts +1 -1
  204. package/src/tasks/reach.ts +2 -2
  205. package/src/tasks/validate.ts +1 -1
  206. package/src/teach/workspace.ts +1 -1
  207. package/src/upstream/cursor.ts +1 -1
  208. package/src/upstream/due.ts +128 -0
  209. package/src/upstream/releases.ts +2 -1
  210. package/standards/context.md +3 -5
  211. package/standards/design.md +2 -3
  212. package/standards/index.md +0 -22
  213. package/standards/publish.md +1 -1
  214. package/standards/skill.md +1 -1
  215. package/tooling/base/reference.md +1 -1
  216. package/tooling/claude/reference.md +6 -20
  217. package/tooling/claude/seeds/.claude/hooks/records-index.sh +8 -20
  218. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +3 -3
  219. package/tooling/claude/seeds/CLAUDE.md +1 -3
  220. package/tooling/claude/seeds/canon/REQUIREMENTS.md +2 -9
  221. package/tooling/web/configs/e2e/screenshot.ts +0 -1
  222. package/claude/skills/auto-ship/references/review-findings.md +0 -20
  223. package/claude/skills/bash-cli-script/REQUIREMENT.md +0 -43
  224. package/claude/skills/bash-cli-script/SKILL.md +0 -53
  225. package/claude/skills/bash-cli-script/references/template.md +0 -19
  226. package/claude/skills/canon-operator/REQUIREMENT.md +0 -62
  227. package/claude/skills/canon-operator/SKILL.md +0 -113
  228. package/claude/skills/canon-rollout/REQUIREMENT.md +0 -59
  229. package/claude/skills/canon-rollout/SKILL.md +0 -140
  230. package/claude/skills/canon-rollout/references/failure-recovery.md +0 -20
  231. package/claude/skills/context-fold/references/anchor-sweep.md +0 -58
  232. package/claude/skills/context-fold/references/wireframe-sweep.md +0 -45
  233. package/claude/skills/create-standard/REQUIREMENT.md +0 -33
  234. package/claude/skills/create-standard/SKILL.md +0 -31
  235. package/claude/skills/design-extract/REQUIREMENT.md +0 -42
  236. package/claude/skills/design-extract/SKILL.md +0 -108
  237. package/claude/skills/draft-and-pick/references/frame.html +0 -118
  238. package/claude/skills/draft-doc/references/wireframes.md +0 -48
  239. package/claude/skills/markdown-propose/REQUIREMENT.md +0 -47
  240. package/claude/skills/markdown-propose/SKILL.md +0 -122
  241. package/claude/skills/markdown-propose/references/format.md +0 -118
  242. package/claude/skills/memory-capture/REQUIREMENT.md +0 -50
  243. package/claude/skills/memory-capture/SKILL.md +0 -117
  244. package/claude/skills/memory-review/REQUIREMENT.md +0 -52
  245. package/claude/skills/memory-review/SKILL.md +0 -129
  246. package/claude/skills/memory-review/references/later-phases.md +0 -118
  247. package/claude/skills/memory-review/references/receipt-format.md +0 -64
  248. package/claude/skills/project-commands/REQUIREMENT.md +0 -42
  249. package/claude/skills/project-commands/SKILL.md +0 -87
  250. package/claude/skills/repo-metadata/REQUIREMENT.md +0 -37
  251. package/claude/skills/repo-metadata/SKILL.md +0 -54
  252. package/claude/skills/restate-plainly/REQUIREMENT.md +0 -41
  253. package/claude/skills/restate-plainly/SKILL.md +0 -41
  254. package/claude/skills/review-branch/REQUIREMENT.md +0 -40
  255. package/claude/skills/review-branch/SKILL.md +0 -137
  256. package/claude/skills/seed-sync/REQUIREMENT.md +0 -45
  257. package/claude/skills/seed-sync/SKILL.md +0 -158
  258. package/claude/skills/sketch-design/REQUIREMENT.md +0 -38
  259. package/claude/skills/sketch-design/SKILL.md +0 -94
  260. package/claude/skills/ux-walkthrough/REQUIREMENT.md +0 -56
  261. package/claude/skills/ux-walkthrough/SKILL.md +0 -53
  262. package/claude/skills/ux-walkthrough/references/builds.md +0 -15
  263. package/claude/skills/ux-walkthrough/references/candidate-pages.md +0 -11
  264. package/claude/skills/ux-walkthrough/references/measuring.md +0 -22
  265. package/claude/skills/ux-walkthrough/references/record.md +0 -36
  266. package/claude/skills/ux-walkthrough/references/relay.md +0 -25
  267. package/docs/agents/architecture-record.md +0 -36
  268. package/docs/agents/review-classification.md +0 -76
  269. package/governance/rules/canon/603-memory.md +0 -11
  270. package/governance/rules/lang/120-bash.md +0 -18
  271. package/governance/rules/standards/architecture.md +0 -13
  272. package/governance/rules/standards/groundwork.md +0 -13
  273. package/governance/rules/standards/intake.md +0 -12
  274. package/governance/rules/standards/memory.md +0 -12
  275. package/governance/rules/standards/ready.md +0 -13
  276. package/governance/rules/standards/requirements.md +0 -12
  277. package/governance/rules/standards/session.md +0 -13
  278. package/governance/rules/standards/tasks.md +0 -15
  279. package/governance/rules/standards/teach.md +0 -14
  280. package/governance/rules/standards/wiki.md +0 -13
  281. package/governance/rules/standards/wireframes.md +0 -13
  282. package/src/autoship/classify.ts +0 -75
  283. package/src/autoship/paths.ts +0 -50
  284. package/src/commands/autoship.ts +0 -129
  285. package/src/commands/repo.ts +0 -393
  286. package/src/context/architecture.ts +0 -654
  287. package/src/context/wireframe-states.ts +0 -238
  288. package/src/repo/metadata.ts +0 -206
  289. package/standards/architecture.md +0 -127
  290. package/standards/memory.md +0 -158
  291. package/standards/mermaid.md +0 -64
  292. package/standards/requirements.md +0 -79
  293. package/standards/wiki.md +0 -77
  294. package/standards/wireframes.md +0 -143
  295. package/tooling/claude/seeds/.claude/memory/index.md +0 -8
  296. package/tooling/claude/seeds/canon/ARCHITECTURE.md +0 -23
  297. package/tooling/claude/seeds/canon/wireframes/index.md +0 -8
  298. /package/{standards → claude/skills/create-skill/references}/skill-practice.md +0 -0
  299. /package/{standards → claude/skills/create-skill/references}/skill-requirement.md +0 -0
  300. /package/{standards → claude/skills/plan-feature/references}/plan-lifecycle.md +0 -0
  301. /package/{standards → claude/skills/plan-groundwork/references}/groundwork-spikes.md +0 -0
  302. /package/{standards → claude/skills/task-board/references}/tasks-decline.md +0 -0
  303. /package/{standards → claude/skills/teach-workspace/references}/glossary.md +0 -0
@@ -1,17 +1,26 @@
1
1
  import { readFile } from 'node:fs/promises'
2
2
  import { join, relative } from 'node:path'
3
- import { wordCount } from '@/context/architecture'
4
3
  import { bodyLines } from '@/markdown/scan'
5
4
  import { surfaceDir } from '@/roots/surface'
6
5
 
7
6
  /**
8
7
  * The record this measures, relative to `root`, resolved at either surface
9
- * root for the reason `architectureRel` gives.
8
+ * root, since a project that has moved holds it under either.
10
9
  */
11
10
  export function requirementsRel(root: string): string {
12
11
  return relative(root, surfaceDir(root, 'REQUIREMENTS.md'))
13
12
  }
14
13
 
14
+ /**
15
+ * Counts whitespace-delimited tokens, the unit the record's word cap is stated
16
+ * in. A code span counts as the tokens it holds, so a line citing a long path
17
+ * pays for it the way a reader does. A caller gating on the count hands in
18
+ * prose with fenced lines already dropped.
19
+ */
20
+ export function wordCount(text: string): number {
21
+ return text.match(/\S+/g)?.length ?? 0
22
+ }
23
+
15
24
  export interface RequirementsReport {
16
25
  readonly rel: string
17
26
  /** The whole record's word count, frontmatter left out. */
@@ -43,8 +52,8 @@ function isMissing(error: unknown): boolean {
43
52
  /**
44
53
  * Measures the record, or reports nothing when the project carries none.
45
54
  *
46
- * Absent rather than zeroed, for the reason `measureArchitecture` gives, and a
47
- * record present and unreadable propagates the same way.
55
+ * Absent rather than zeroed, so a project with no record is never told it holds an
56
+ * empty one, and a record present and unreadable propagates.
48
57
  */
49
58
  export async function measureRequirements(
50
59
  root: string,
@@ -35,8 +35,8 @@ export type CountsReport =
35
35
 
36
36
  /**
37
37
  * A calendar date, which reads a sentence as a historical record rather than
38
- * a live claim about the tree. `canon/ARCHITECTURE.md` and the context
39
- * entries carry a figure this way deliberately, and every one of them stays
38
+ * a live claim about the tree. The context entries
39
+ * carry a figure this way deliberately, and every one of them stays
40
40
  * correct forever, so a sentence carrying one is read past rather than
41
41
  * matched.
42
42
  */
@@ -142,7 +142,7 @@ const QUANTIFIERS = ['all']
142
142
  * match at all. No syntactic rule tells a qualifier that narrows a catalog
143
143
  * from one that only restates it, and the sentence was true when written, so
144
144
  * the fix was not a narrower matcher: the clause was dated as a past state,
145
- * the way `canon/ARCHITECTURE.md` dates its own figures, which is the class
145
+ * the way a context entry dates its own figures, which is the class
146
146
  * the date exclusion below already reads past.
147
147
  */
148
148
  const ARTICLES = ['the', 'a', 'an']
@@ -250,8 +250,8 @@ const TEACH_CHROME: Component = {
250
250
  'navigation shared by every teach page. Recovered from two gitignored',
251
251
  'course.css files that predate a regression that dropped this layer from the',
252
252
  "generator, and rewritten onto this module's tokens rather than the",
253
- 'incompatible palette they carried. See canon/wireframes/teach/chrome.md for',
254
- 'the shape.',
253
+ 'incompatible palette they carried. See canon/context/features/teach/chrome.md',
254
+ 'for the shape.',
255
255
  ].join('\n '),
256
256
  reads: [
257
257
  '--color-background',
@@ -1,5 +1,3 @@
1
- const SLUG_FALLBACK = 'general'
2
- const SLUG_MAX_LENGTH = 40
3
1
  const TITLE_MAX_LENGTH = 72
4
2
 
5
3
  /**
@@ -79,16 +77,6 @@ function surfaceField(body: string): string | undefined {
79
77
  return readField(body, 'Surface')?.split('\n')[0]?.trim()
80
78
  }
81
79
 
82
- export function deriveSlug(body: string): string {
83
- const slug = (surfaceField(body) ?? '')
84
- .toLowerCase()
85
- .replace(/[^a-z0-9]+/g, '-')
86
- .replace(/^-+|-+$/g, '')
87
- .slice(0, SLUG_MAX_LENGTH)
88
- .replace(/-+$/g, '')
89
- return slug || SLUG_FALLBACK
90
- }
91
-
92
80
  /**
93
81
  * Each label must exist on the toolkit repository, because `gh issue create`
94
82
  * refuses the whole issue on an unknown label rather than dropping it.
@@ -7,14 +7,6 @@ import {
7
7
  type ClientCommand,
8
8
  clientCommandCitationsIn,
9
9
  } from '@/gate/client-commands'
10
- import {
11
- isOverCount,
12
- isOverLength,
13
- isOverRisks,
14
- measureArchitecture,
15
- missingRevisit,
16
- overWords,
17
- } from '@/context/architecture'
18
10
  import { isOverWordCap, measureRequirements } from '@/context/requirements'
19
11
  import {
20
12
  auditSkills,
@@ -253,62 +245,19 @@ function citedPaths(record: { paths?: unknown } | undefined): string[] {
253
245
  }
254
246
 
255
247
  /**
256
- * The canonical records against the caps each states for itself. The
257
- * architecture record carries five: the line ceiling its allowances derive,
258
- * the entry cap, the revisit sentence closing each decision, the words a
259
- * decision may hold, and the bullets its Risks section may hold. The
260
- * requirements record carries one, its own word count.
248
+ * The requirements record against the word cap it states for itself.
261
249
  *
262
250
  * Read in-process rather than through `context audit`, whose one gating stage
263
- * here runs `--citations-only` and never opens either record, which is why
264
- * the line ceiling went unenforced by `bun run check` until this stage. A
265
- * project carrying neither record, or records stating none of the caps,
266
- * passes, since each rule belongs to the record rather than to the toolkit.
251
+ * here runs `--citations-only` and never opens the record. A project carrying
252
+ * no record, or a record stating no cap, passes, since the rule belongs to the
253
+ * record rather than to the toolkit.
267
254
  */
268
255
  export const canonicalRecords: Measure = async (ctx) => {
269
- const [report, requirements] = await Promise.all([
270
- measureArchitecture(ctx.root),
271
- measureRequirements(ctx.root),
272
- ])
256
+ const requirements = await measureRequirements(ctx.root)
273
257
 
274
258
  const failures: string[] = []
275
259
  const emissions: Emission[] = []
276
260
 
277
- if (report === undefined) {
278
- emissions.push(info('No architecture record to measure'))
279
- } else {
280
- const decisions = report.decisions.length
281
- const lackingRevisit = missingRevisit(report)
282
- const longDecisions = overWords(report)
283
- failures.push(
284
- ...[
285
- isOverCount(report) &&
286
- `${decisions} decisions against a cap of ${report.entryCap}. Merge two or retire one in ${report.rel}, never compress.`,
287
- isOverLength(report) &&
288
- `${report.lines} lines against a ceiling of ${report.ceiling} in ${report.rel}.`,
289
- lackingRevisit.length > 0 &&
290
- `No revisit sentence in ${lackingRevisit.map((heading) => `"${heading}"`).join(', ')}. Close each with one opening "Revisit when" in ${report.rel}.`,
291
- longDecisions.length > 0 &&
292
- `${longDecisions.map((entry) => `"${entry.heading}" (${entry.words})`).join(', ')} past ${report.wordCap} words a decision in ${report.rel}. Cut the reasoning to the trade, never split one decision across two headings.`,
293
- isOverRisks(report) &&
294
- `${report.risksBullets} risk bullets against a cap of ${report.riskCap} in ${report.rel}. Drop what is no longer open.`,
295
- ].filter((failure): failure is string => typeof failure === 'string'),
296
- )
297
-
298
- const cap =
299
- report.entryCap === undefined
300
- ? 'no entry cap stated'
301
- : `a cap of ${report.entryCap}`
302
- emissions.push(info(`${decisions} decisions against ${cap}`))
303
- if (report.riskCap !== undefined && report.risksBullets === undefined) {
304
- emissions.push(
305
- info(
306
- `No Risks section to count against a cap of ${report.riskCap} in ${report.rel}`,
307
- ),
308
- )
309
- }
310
- }
311
-
312
261
  if (requirements === undefined) {
313
262
  emissions.push(info('No requirements record to measure'))
314
263
  } else if (isOverWordCap(requirements)) {
@@ -415,7 +364,7 @@ export const skillProvenance: Measure = async (ctx) => {
415
364
  /**
416
365
  * Every listed practice skill against the closing sections, and every skill on
417
366
  * the wider ledger list against the ledger, both of which
418
- * `standards/skill-practice.md` requires.
367
+ * `claude/skills/create-skill/references/skill-practice.md` requires.
419
368
  *
420
369
  * Read in-process for the reason `skillProvenance` is, so the verb keeps its
421
370
  * missing-requirement-only exit. The list names shipped folders alone, so a
@@ -6,6 +6,7 @@ import { isMarked } from '@/sweep/exempt-marker'
6
6
  import { gitEnv } from '@/git/env'
7
7
  import { listRuleFiles } from '@/gov/payload'
8
8
  import { parseFrontmatter } from '@/indexes/frontmatter'
9
+ import { movedStandardPath } from '@/standards/read'
9
10
  import { findBacktickSpans } from '@/markdown/backticks'
10
11
 
11
12
  /**
@@ -177,8 +178,9 @@ export function classifySpan(span: string): CitationForm | undefined {
177
178
  *
178
179
  * A standard name takes the authoring root alone, matching `standardRoots` in
179
180
  * `@/standards/read`, which reads `standards/` at the working root and then the
180
- * package corpus. This verb refuses a tree holding no rule corpus, so it runs
181
- * only where those two roots are one directory. `internal/standards/` is
181
+ * package corpus, plus the skill reference a retired name moved to. This verb
182
+ * refuses a tree holding no rule corpus, so it runs only where those two roots
183
+ * are one directory. `internal/standards/` is
182
184
  * deliberately absent: `canon standards <name>` never reaches it, so admitting it
183
185
  * here would pass a citation that refuses for the session opening it, which is a
184
186
  * gate failing open.
@@ -191,7 +193,11 @@ function candidatesFor(
191
193
  cited: string,
192
194
  ruleFile: string,
193
195
  ): string[] {
194
- if (form === 'standard') return [join('standards', `${cited}.md`)]
196
+ if (form === 'standard') {
197
+ const moved = movedStandardPath(cited)
198
+
199
+ return [join('standards', `${cited}.md`), ...(moved ? [moved] : [])]
200
+ }
195
201
  if (form === 'sibling') return [join(dirname(ruleFile), cited)]
196
202
  return [cited]
197
203
  }
@@ -36,8 +36,8 @@ const SKILL_ROOTS: readonly string[] = [SHIPPED_SKILLS_REL, PROJECT_SKILLS_REL]
36
36
  /**
37
37
  * Path pairs whose duplication is deliberate and already recorded.
38
38
  *
39
- * The seed is authored from the always-loaded file and `seed-sync`
40
- * exists to reconcile the two, so a bullet appearing in both is the design
39
+ * The seed is authored from the always-loaded file and a hand merge
40
+ * reconciles the two, so a bullet appearing in both is the design
41
41
  * rather than a defect. Excluding by pair rather than by content is what the
42
42
  * plan settled on: the duplication is a location fact this repository already
43
43
  * records, and a content test would have to rediscover it on every run.
@@ -611,7 +611,7 @@ function authorityFor(
611
611
  if (candidate.kind === 'seed') {
612
612
  return {
613
613
  authority: 'claude-md',
614
- reason: `${INSTRUCTIONS_REL} is authored first and the seed carries it to a target, so an edit starts there and reaches the seed through seed-sync`,
614
+ reason: `${INSTRUCTIONS_REL} is authored first and the seed carries it to a target, so an edit starts there and reaches the seed through a hand merge`,
615
615
  }
616
616
  }
617
617
 
@@ -6,7 +6,7 @@ const HEADING = /^#{1,6}\s/
6
6
  * A section marker holding its line alone, which is the signpost a document
7
7
  * uses where its own template asks for bold rather than a heading.
8
8
  *
9
- * `standards/plan.md` gives `## Summary` a heading and marks the four sections
9
+ * `claude/skills/plan-feature/references/plan.md` gives `## Summary` a heading and marks the four sections
10
10
  * below it this way, so a conforming plan read as one run from its first line
11
11
  * to its last and all seven live plans reported past the depth checkpoint, at
12
12
  * 106 to 166 rendered lines. A measure firing on a whole corpus says nothing
@@ -659,7 +659,7 @@ function measureParagraph(block: readonly BodyLine[]): CadenceFinding {
659
659
  *
660
660
  * `compromise` reported 2 percent. It tags a fronted past participle as a
661
661
  * finite past-tense verb, so all twelve of those sentences in
662
- * `.claude/ARCHITECTURE.md` read as carrying one, eleven opening `Measured at`
662
+ * the repository's architecture record at that commit read as carrying one, eleven opening `Measured at`
663
663
  * and one `Overturned by`, which is the exact shape the measure exists to
664
664
  * catch. It is not even consistent with itself there: `Measured at` tags a verb
665
665
  * and `Measured against` tags an adjective.
@@ -346,9 +346,9 @@ function entriesOf(dir: string, isDirectory: boolean): string[] {
346
346
  }
347
347
 
348
348
  /**
349
- * What `draft-and-pick` Step 6 and `sketch-design` write into the folder they
350
- * archive a choice to: a capture per arm, named `arm-<id>`, and a design
351
- * handoff. A folder carrying neither is evidence.
349
+ * What `draft-and-pick` Step 6 writes into the folder it archives a choice to:
350
+ * a capture per canvas frame, named `arm-<id>`. A design handoff marks a pick
351
+ * folder an earlier skill wrote. A folder carrying neither is evidence.
352
352
  */
353
353
  const PICK_FILE = /^(?:arm-[^.]+\..+|design-handoff\.md)$/
354
354
 
@@ -4,7 +4,7 @@ import { recordDir } from '@/roots/record'
4
4
 
5
5
  /**
6
6
  * The two record folders that share one ordinal sequence, per
7
- * `standards/intake.md` and `standards/groundwork.md`. A folder opened as
7
+ * `claude/skills/plan-intake/references/intake.md` and `claude/skills/plan-groundwork/references/groundwork.md`. A folder opened as
8
8
  * either kind blocks the same number for the other, so the two read together
9
9
  * rather than each keeping a count of its own.
10
10
  */
@@ -35,7 +35,8 @@ const LEGACY_MOVES: ReadonlyMap<string, string> = new Map([
35
35
  * Scratch-root names a different migration moves out of scratch for good,
36
36
  * derived from that migration's own table rather than duplicated in a second
37
37
  * hand-kept list. `RECORD_LAYOUT_MOVES` names `memory-archive`, the
38
- * retired-entry archive `603-memory` says never to delete, and `PROMOTED_FOLDERS`
38
+ * retired-entry archive of the memory kind, which is never offered for deletion,
39
+ * and `PROMOTED_FOLDERS`
39
40
  * names an evidence folder a durable record cites by name. A project that has
40
41
  * not run either migration still carries these at the scratch root, where an
41
42
  * ordinary slug's own age test would eventually offer them.
@@ -6,8 +6,8 @@ import { gitEnv } from '@/git/env'
6
6
  import { readReviewed } from '@/records/stale'
7
7
  import { surfaceDir } from '@/roots/surface'
8
8
 
9
- /** The two records whose drift is identity rather than a stale path. */
10
- const CANONICAL_DOCS = ['REQUIREMENTS.md', 'ARCHITECTURE.md'] as const
9
+ /** The record whose drift is identity rather than a stale path. */
10
+ const CANONICAL_DOCS = ['REQUIREMENTS.md'] as const
11
11
 
12
12
  export interface CanonicalDoc {
13
13
  /** Relative to the root, at whichever surface root carries the doc. */
@@ -56,7 +56,7 @@ const CANON_SCRATCH = 'tmp'
56
56
  * Everything absent from this list is committed and never lands under the
57
57
  * record root, which is the rule the move ran on. `rules`, `skills`, and
58
58
  * `hooks` stay under `.claude/` because the vendor reads them there, while
59
- * `context`, `wireframes`, and the loose documents are tracked surfaces that
59
+ * `context` and the loose documents are tracked surfaces that
60
60
  * `surface.ts` resolves under `canon/` instead. A seed and a
61
61
  * superseded-layout report each ask this rather than assuming a root.
62
62
  */
@@ -8,10 +8,19 @@ import {
8
8
  readFileSync,
9
9
  statSync,
10
10
  writeFileSync,
11
+ writeSync,
11
12
  } from 'node:fs'
12
13
  import { createRequire } from 'node:module'
13
14
  import { dirname, join, relative } from 'node:path'
14
- import { logError, logInfo, logStep, logWarn, palette, select } from '@/cli/ui'
15
+ import {
16
+ logError,
17
+ logInfo,
18
+ logStep,
19
+ logWarn,
20
+ palette,
21
+ pipeOutput,
22
+ select,
23
+ } from '@/cli/ui'
15
24
 
16
25
  /**
17
26
  * The TypeScript form of a sandbox scenario: `sandbox/<category>/<command>.ts`,
@@ -33,7 +42,7 @@ export const FIXTURE_SUFFIX = '.fixture'
33
42
  /** A scenario whose only arm carries this name routes none, as a bash file with no picker did. */
34
43
  export const DEFAULT_ARM_NAME = 'default'
35
44
 
36
- /** Every bash scenario passed this prompt to `select_or_route_scenario`. */
45
+ /** What a bash scenario passed to `select_or_route_scenario` unless it named another. */
37
46
  const ROUTE_PROMPT = 'Which scenario?'
38
47
 
39
48
  /** Every scenario that sets the anchor pushes to this one throwaway repository. */
@@ -50,6 +59,14 @@ export interface ScenarioDefinition {
50
59
  readonly config?: Readonly<Record<string, string>>
51
60
  /** Stages the tree from the anchor fixture and names the throwaway remote. */
52
61
  readonly anchor?: boolean
62
+ /**
63
+ * Stages and narrates what the bash form did ahead of `select_or_route_scenario`,
64
+ * so the picker's echo lands after it on the frame. Runs before the arm is
65
+ * routed, which leaves `ctx.arm` undefined.
66
+ */
67
+ readonly prepare?: ArmSetup
68
+ /** The picker's message, for a bash scenario that passed something other than the default. */
69
+ readonly prompt?: string
53
70
  /** In routing order, so the first arm is the one a headless caller gets. */
54
71
  readonly arms: Readonly<Record<string, ArmSetup>>
55
72
  }
@@ -144,7 +161,7 @@ export function loadScenario(file: string): ScenarioDefinition {
144
161
  if (!isRecord(definition))
145
162
  throw new Error(`${file} has no default export from scenario()`)
146
163
 
147
- const { arms, config, anchor } = definition
164
+ const { arms, config, anchor, prepare, prompt } = definition
148
165
  if (!isRecord(arms) || Object.keys(arms).length === 0)
149
166
  throw new Error(`${file} declares no arms`)
150
167
  for (const [name, setup] of Object.entries(arms))
@@ -158,6 +175,10 @@ export function loadScenario(file: string): ScenarioDefinition {
158
175
  throw new Error(`${file} config holds a value that is not a string`)
159
176
  if (anchor !== undefined && typeof anchor !== 'boolean')
160
177
  throw new Error(`${file} anchor is not a boolean`)
178
+ if (prepare !== undefined && typeof prepare !== 'function')
179
+ throw new Error(`${file} prepare is not a function`)
180
+ if (prompt !== undefined && typeof prompt !== 'string')
181
+ throw new Error(`${file} prompt is not a string`)
161
182
 
162
183
  return definition as unknown as ScenarioDefinition
163
184
  }
@@ -225,6 +246,24 @@ export interface RunOptions {
225
246
  readonly stderr?: 'inherit' | 'ignore'
226
247
  /** Returns a failing status rather than stopping the arm, the `|| true` of the bash form. */
227
248
  readonly allowFailure?: boolean
249
+ /** Set for this command alone, the `NAME=value command` prefix of the bash form. */
250
+ readonly env?: Readonly<Record<string, string>>
251
+ }
252
+
253
+ export interface CaptureOptions {
254
+ /** Relative to the tree, the `cd` of a bash subshell. */
255
+ readonly cwd?: string
256
+ /** Fed to the command's stdin, the `<file` or `<<<` of the bash form. */
257
+ readonly input?: string
258
+ readonly env?: Readonly<Record<string, string>>
259
+ /** `inherit` leaves the stream on the terminal, where `pipe` hands it back for a file. */
260
+ readonly stderr?: 'pipe' | 'inherit' | 'ignore'
261
+ }
262
+
263
+ export interface Captured {
264
+ readonly status: number
265
+ readonly stdout: string
266
+ readonly stderr: string
228
267
  }
229
268
 
230
269
  export interface StageContext {
@@ -240,6 +279,8 @@ export interface StageContext {
240
279
  warn(message: string): void
241
280
  /** A bare frame line, the `echo -e "${GREY}│${NC}"` of the bash form. */
242
281
  bar(): void
282
+ /** Indents borrowed output inside the frame, the `pipe_output` of the bash form. */
283
+ pipe(text: string): void
243
284
  }
244
285
  fixtures(category: string, command: string, arm: string, stage: string): void
245
286
  git(...args: string[]): void
@@ -248,6 +289,18 @@ export interface StageContext {
248
289
  run(command: string, args: readonly string[], options?: RunOptions): number
249
290
  /** Runs a command and returns its stdout, stopping the arm on a failure. */
250
291
  read(command: string, args: readonly string[], options?: RunOptions): string
292
+ /**
293
+ * Runs a command and returns both streams with its status, never stopping the
294
+ * arm. For the `>file 2>file || status=$?` shape, where the outcome is the
295
+ * record the arm exists to hold.
296
+ */
297
+ capture(
298
+ command: string,
299
+ args: readonly string[],
300
+ options?: CaptureOptions,
301
+ ): Captured
302
+ /** Writes to a stream the way a bare `cat` or `echo` did, in order with the commands around it. */
303
+ print(text: string, stream?: 'stdout' | 'stderr'): void
251
304
  write(path: string, content: string): void
252
305
  append(path: string, content: string): void
253
306
  mkdir(path: string): void
@@ -291,14 +344,24 @@ export function createStageContext(options: ContextOptions): StageContext {
291
344
  args: readonly string[],
292
345
  stdout: 'inherit' | 'ignore' | 'pipe',
293
346
  stderr: 'inherit' | 'ignore' | 'pipe',
347
+ extra: {
348
+ cwd?: string
349
+ input?: string
350
+ env?: Readonly<Record<string, string>>
351
+ } = {},
294
352
  ) => {
295
- const stdio: StdioOptions = ['ignore', stdout, stderr]
353
+ const stdio: StdioOptions = [
354
+ extra.input === undefined ? 'ignore' : 'pipe',
355
+ stdout,
356
+ stderr,
357
+ ]
296
358
 
297
359
  return spawnSync(command, [...args], {
298
- cwd: dir,
299
- env,
360
+ cwd: extra.cwd === undefined ? dir : join(dir, extra.cwd),
361
+ env: extra.env === undefined ? env : { ...env, ...extra.env },
300
362
  encoding: 'utf8',
301
363
  stdio,
364
+ input: extra.input,
302
365
  })
303
366
  }
304
367
 
@@ -312,6 +375,7 @@ export function createStageContext(options: ContextOptions): StageContext {
312
375
  args,
313
376
  runOptions.stdout ?? 'inherit',
314
377
  runOptions.stderr ?? 'inherit',
378
+ { env: runOptions.env },
315
379
  )
316
380
  const status = result.status ?? 1
317
381
  if (status !== 0 && runOptions.allowFailure !== true)
@@ -325,7 +389,13 @@ export function createStageContext(options: ContextOptions): StageContext {
325
389
  args: readonly string[],
326
390
  runOptions: RunOptions = {},
327
391
  ): string => {
328
- const result = spawn(command, args, 'pipe', runOptions.stderr ?? 'inherit')
392
+ const result = spawn(
393
+ command,
394
+ args,
395
+ 'pipe',
396
+ runOptions.stderr ?? 'inherit',
397
+ { env: runOptions.env },
398
+ )
329
399
  const status = result.status ?? 1
330
400
  if (status !== 0 && runOptions.allowFailure !== true)
331
401
  throw new ScenarioStop(status, 'exited')
@@ -333,6 +403,26 @@ export function createStageContext(options: ContextOptions): StageContext {
333
403
  return status === 0 ? result.stdout : ''
334
404
  }
335
405
 
406
+ const capture = (
407
+ command: string,
408
+ args: readonly string[],
409
+ captureOptions: CaptureOptions = {},
410
+ ): Captured => {
411
+ const result = spawn(
412
+ command,
413
+ args,
414
+ 'pipe',
415
+ captureOptions.stderr ?? 'pipe',
416
+ captureOptions,
417
+ )
418
+
419
+ return {
420
+ status: result.status ?? 1,
421
+ stdout: result.stdout ?? '',
422
+ stderr: result.stderr ?? '',
423
+ }
424
+ }
425
+
336
426
  const git = (...args: string[]): void => {
337
427
  run('git', args)
338
428
  }
@@ -476,12 +566,20 @@ export function createStageContext(options: ContextOptions): StageContext {
476
566
  const { GREY, NC } = palette(process.stderr)
477
567
  process.stderr.write(`${GREY}│${NC}\n`)
478
568
  },
569
+ // `pipe_output` read lines, so an empty body printed nothing at all.
570
+ pipe: (text) => {
571
+ if (text !== '') pipeOutput(text)
572
+ },
479
573
  },
480
574
  fixtures,
481
575
  git,
482
576
  commit: (subject) => git('commit', '-m', subject, '--no-verify', '-q'),
483
577
  run,
484
578
  read,
579
+ capture,
580
+ print: (text, stream = 'stdout') => {
581
+ writeSync(stream === 'stdout' ? 1 : 2, text)
582
+ },
485
583
  write: (path, content) => {
486
584
  mkdirSync(dirname(inTree(path)), { recursive: true })
487
585
  writeFileSync(inTree(path), content)
@@ -510,11 +608,12 @@ export function createStageContext(options: ContextOptions): StageContext {
510
608
  async function routeArm(
511
609
  names: readonly string[],
512
610
  env: NodeJS.ProcessEnv,
611
+ prompt: string,
513
612
  ): Promise<string> {
514
613
  const echo = (name: string): string => {
515
614
  const { GREY, NC, WHITE } = palette(process.stderr)
516
615
  process.stderr.write(
517
- `${GREY}│${NC}\n${GREY}◇${NC} ${ROUTE_PROMPT} ${WHITE}${name}${NC}\n`,
616
+ `${GREY}│${NC}\n${GREY}◇${NC} ${prompt} ${WHITE}${name}${NC}\n`,
518
617
  )
519
618
 
520
619
  return name
@@ -533,13 +632,13 @@ async function routeArm(
533
632
 
534
633
  if (!process.stdin.isTTY) {
535
634
  logError(
536
- `${ROUTE_PROMPT} requires a TTY. Pass an argument or set CANON_NON_INTERACTIVE=1.`,
635
+ `${prompt} requires a TTY. Pass an argument or set CANON_NON_INTERACTIVE=1.`,
537
636
  )
538
637
  throw new ScenarioStop(1, 'exited')
539
638
  }
540
639
 
541
640
  return select({
542
- message: ROUTE_PROMPT,
641
+ message: prompt,
543
642
  options: names.map((name) => ({ value: name, label: name })),
544
643
  })
545
644
  }
@@ -560,8 +659,13 @@ export async function stageScenarioInProcess(
560
659
  ): Promise<StageOutcome> {
561
660
  try {
562
661
  const names = armNames(definition)
662
+ await definition.prepare?.(
663
+ createStageContext({ ...options, arm: undefined }),
664
+ )
563
665
  const name =
564
- names.length === 0 ? DEFAULT_ARM_NAME : await routeArm(names, options.env)
666
+ names.length === 0
667
+ ? DEFAULT_ARM_NAME
668
+ : await routeArm(names, options.env, definition.prompt ?? ROUTE_PROMPT)
565
669
  const setup = Object.hasOwn(definition.arms, name)
566
670
  ? definition.arms[name]
567
671
  : undefined
@@ -168,8 +168,8 @@ const DOCS_PATH = /(?<![\w./-])docs\/[^\s`)\]]*\.md\b/g
168
168
  * Reported unless `isStandardsPathReportable` reads the match as a
169
169
  * deliberate placeholder rather than a real citation. A body illustrating
170
170
  * the shape a project's own `standards/<name>.md` takes writes a bracketed
171
- * token this pattern also matches, such as `standards/<slug>.md` in
172
- * `create-standard/SKILL.md`, which names no real file. A bare,
171
+ * token this pattern also matches, such as `standards/<slug>.md`
172
+ * in a skill that drafts one, which names no real file. A bare,
173
173
  * non-bracketed match now reports whether or not the file exists, unlike
174
174
  * `DOCS_PATH`, which still gates on existence: a same-repository citation
175
175
  * under this corpus names no target project's own tree the way a `docs/`
@@ -206,7 +206,7 @@ const PHASE_LABEL = /\bv\d+\.\d+(?!\.\d)\b/g
206
206
  *
207
207
  * Anchored on the trailing `\d{3}-[\w-]+\.md` rather than on the bare
208
208
  * `.claude/rules/` prefix, which is what keeps a folder mention carrying no
209
- * number, such as `create-rule`, `memory-review`, and `target-setup` already
209
+ * number, such as `create-rule`, `target-check`, and `target-setup` already
210
210
  * write correctly, from matching. The segment group between `rules/` and the
211
211
  * numbered file admits both a governance-namespace path
212
212
  * (`canon/canon/604-scratch.md`) and a project-namespace one
@@ -59,7 +59,38 @@ export function standardRoots(root: string): StandardRoot[] {
59
59
  * map goes with the verb in the last slice of the retirement.
60
60
  */
61
61
  const MOVED_STANDARDS: Readonly<Record<string, string>> = {
62
+ board: 'claude/skills/task-board/references/board.md',
62
63
  markdown: 'claude/skills/markdown-craft/references/markdown.md',
64
+ canvas: 'claude/skills/canvas/references/canvas.md',
65
+ glossary: 'claude/skills/teach-workspace/references/glossary.md',
66
+ groundwork: 'claude/skills/plan-groundwork/references/groundwork.md',
67
+ 'groundwork-spikes':
68
+ 'claude/skills/plan-groundwork/references/groundwork-spikes.md',
69
+ intake: 'claude/skills/plan-intake/references/intake.md',
70
+ mermaid: 'claude/skills/draft-figure/references/mermaid.md',
71
+ plan: 'claude/skills/plan-feature/references/plan.md',
72
+ 'plan-lifecycle': 'claude/skills/plan-feature/references/plan-lifecycle.md',
73
+ ready: 'claude/skills/draft-ready/references/ready.md',
74
+ requirements: 'claude/skills/draft-doc/references/requirements.md',
75
+ 'skill-paths': 'claude/skills/create-skill/references/skill-paths.md',
76
+ 'skill-practice': 'claude/skills/create-skill/references/skill-practice.md',
77
+ session: 'claude/skills/session-map/references/session.md',
78
+ 'skill-requirement':
79
+ 'claude/skills/create-skill/references/skill-requirement.md',
80
+ tasks: 'claude/skills/task-board/references/tasks.md',
81
+ 'tasks-decline': 'claude/skills/task-board/references/tasks-decline.md',
82
+ teach: 'claude/skills/teach-workspace/references/teach.md',
83
+ wiki: 'claude/skills/draft-doc/references/wiki.md',
84
+ }
85
+
86
+ /**
87
+ * The package-relative path a retired standard now lives at, so a caller that
88
+ * resolves against a root of its own tests the same map the verb reads.
89
+ */
90
+ export function movedStandardPath(name: string): string | undefined {
91
+ if (!Object.hasOwn(MOVED_STANDARDS, name)) return undefined
92
+
93
+ return MOVED_STANDARDS[name]
63
94
  }
64
95
 
65
96
  function filename(name: string): string {
@@ -34,7 +34,7 @@ export interface SeedsReport {
34
34
  * Classifies every seed the toolkit ships against the target's copy, and never
35
35
  * returns a change. Seeds are copy-once files a project is expected to edit, so
36
36
  * the engine's copy path would overwrite `CLAUDE.md` wholesale. Reporting alone
37
- * is what lets `seed-sync` merge one section at a time instead.
37
+ * is what lets an operator merge one section at a time by hand instead.
38
38
  *
39
39
  * Attribution reuses the history reader rather than the engine's own recovery
40
40
  * pass, which is private and takes a `SyncAdapter` seeds have no way to supply.