@erclx/canon 5.30.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 (343) 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/build-in-slices/REQUIREMENT.md +1 -1
  13. package/claude/skills/build-in-slices/references/adopted.md +1 -1
  14. package/claude/skills/canon-cli/REQUIREMENT.md +4 -6
  15. package/claude/skills/canon-cli/SKILL.md +2 -2
  16. package/claude/skills/canon-feedback/REQUIREMENT.md +1 -1
  17. package/claude/skills/canon-feedback/SKILL.md +10 -13
  18. package/claude/skills/canon-feedback-triage/REQUIREMENT.md +13 -14
  19. package/claude/skills/canon-feedback-triage/SKILL.md +50 -29
  20. package/claude/skills/canvas/REQUIREMENT.md +3 -4
  21. package/claude/skills/canvas/SKILL.md +5 -5
  22. package/{standards → claude/skills/canvas/references}/canvas.md +12 -1
  23. package/claude/skills/ci-workflow/REQUIREMENT.md +0 -1
  24. package/claude/skills/context-fold/REQUIREMENT.md +8 -20
  25. package/claude/skills/context-fold/SKILL.md +21 -45
  26. package/claude/skills/context-fold/references/classify.md +7 -8
  27. package/claude/skills/context-fold/references/completion-report.md +2 -6
  28. package/claude/skills/context-fold/references/context-refresh.md +5 -15
  29. package/claude/skills/context-fold/references/promoted-pages.md +3 -3
  30. package/claude/skills/context-fold/references/receipt-sweep.md +6 -17
  31. package/claude/skills/context-fold/references/unusable-baseline.md +4 -6
  32. package/claude/skills/create-rule/REQUIREMENT.md +0 -1
  33. package/claude/skills/create-skill/REQUIREMENT.md +6 -4
  34. package/claude/skills/create-skill/SKILL.md +9 -8
  35. package/{standards → claude/skills/create-skill/references}/skill-paths.md +1 -0
  36. package/claude/skills/decision-escalate/REQUIREMENT.md +1 -1
  37. package/claude/skills/decision-escalate/SKILL.md +1 -1
  38. package/claude/skills/design-taste/REQUIREMENT.md +0 -2
  39. package/claude/skills/design-taste/SKILL.md +2 -4
  40. package/claude/skills/design-taste/references/vocabulary.md +1 -1
  41. package/claude/skills/docs-sync/REQUIREMENT.md +3 -3
  42. package/claude/skills/docs-sync/SKILL.md +1 -1
  43. package/claude/skills/document-health/REQUIREMENT.md +2 -2
  44. package/claude/skills/document-health/SKILL.md +4 -5
  45. package/claude/skills/document-health/references/axes.md +4 -6
  46. package/claude/skills/draft-and-pick/REQUIREMENT.md +16 -14
  47. package/claude/skills/draft-and-pick/SKILL.md +40 -32
  48. package/claude/skills/draft-and-pick/references/live-arms.md +9 -9
  49. package/claude/skills/draft-doc/REQUIREMENT.md +5 -5
  50. package/claude/skills/draft-doc/SKILL.md +10 -11
  51. package/claude/skills/draft-doc/references/requirements.md +61 -0
  52. package/claude/skills/draft-doc/references/wiki-kind.md +53 -0
  53. package/claude/skills/draft-doc/references/wiki.md +50 -30
  54. package/claude/skills/draft-figure/REQUIREMENT.md +2 -2
  55. package/claude/skills/draft-figure/SKILL.md +5 -5
  56. package/claude/skills/draft-figure/references/architecture-set.md +4 -4
  57. package/claude/skills/draft-figure/references/hand-drawn.md +1 -1
  58. package/claude/skills/draft-figure/references/mermaid-path.md +55 -0
  59. package/claude/skills/draft-figure/references/mermaid.md +43 -34
  60. package/claude/skills/draft-identity/REQUIREMENT.md +1 -1
  61. package/claude/skills/draft-identity/SKILL.md +9 -7
  62. package/claude/skills/draft-ready/REQUIREMENT.md +1 -1
  63. package/claude/skills/draft-ready/SKILL.md +6 -6
  64. package/claude/skills/draft-ready/references/assembly.md +1 -1
  65. package/{standards → claude/skills/draft-ready/references}/ready.md +13 -5
  66. package/claude/skills/git-issue/SKILL.md +1 -1
  67. package/claude/skills/git-issue/references/issue.md +3 -3
  68. package/claude/skills/git-pr/SKILL.md +1 -1
  69. package/claude/skills/git-ship/REQUIREMENT.md +0 -2
  70. package/claude/skills/git-ship/SKILL.md +16 -24
  71. package/claude/skills/git-stage/SKILL.md +1 -1
  72. package/claude/skills/markdown-craft/REQUIREMENT.md +23 -0
  73. package/claude/skills/markdown-craft/SKILL.md +17 -0
  74. package/{standards → claude/skills/markdown-craft/references}/markdown.md +14 -7
  75. package/claude/skills/plan-feature/REQUIREMENT.md +2 -1
  76. package/claude/skills/plan-feature/SKILL.md +14 -8
  77. package/{standards → claude/skills/plan-feature/references}/plan.md +33 -21
  78. package/claude/skills/plan-groundwork/SKILL.md +5 -6
  79. package/{standards → claude/skills/plan-groundwork/references}/groundwork.md +17 -4
  80. package/claude/skills/plan-intake/SKILL.md +2 -3
  81. package/{standards → claude/skills/plan-intake/references}/intake.md +17 -2
  82. package/claude/skills/plan-intake-answer/REQUIREMENT.md +1 -1
  83. package/claude/skills/plan-intake-answer/SKILL.md +1 -1
  84. package/claude/skills/review-address/SKILL.md +2 -2
  85. package/claude/skills/review-address/references/post-review-findings.md +1 -1
  86. package/claude/skills/review-craft/REQUIREMENT.md +3 -3
  87. package/claude/skills/review-craft/SKILL.md +2 -2
  88. package/claude/skills/review-craft/references/ui.md +4 -4
  89. package/claude/skills/review-pr/REQUIREMENT.md +1 -1
  90. package/claude/skills/review-pr/SKILL.md +5 -7
  91. package/claude/skills/review-pr/references/post.md +2 -2
  92. package/claude/skills/review-ui/REQUIREMENT.md +0 -1
  93. package/claude/skills/review-ui/SKILL.md +1 -1
  94. package/claude/skills/review-ui/references/post.md +1 -1
  95. package/claude/skills/role-orchestrator/REQUIREMENT.md +0 -3
  96. package/claude/skills/role-orchestrator/SKILL.md +8 -8
  97. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +4 -3
  98. package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +7 -10
  99. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
  100. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +0 -3
  101. package/claude/skills/role-orchestrator/references/orchestrator-resume.md +1 -1
  102. package/claude/skills/role-planner/SKILL.md +2 -2
  103. package/claude/skills/role-reviewer/SKILL.md +1 -1
  104. package/claude/skills/role-worker/SKILL.md +2 -2
  105. package/claude/skills/session-compact/REQUIREMENT.md +1 -4
  106. package/claude/skills/session-compact/SKILL.md +3 -13
  107. package/claude/skills/session-compact/references/handoff-note.md +2 -2
  108. package/claude/skills/session-map/REQUIREMENT.md +1 -9
  109. package/claude/skills/session-map/SKILL.md +8 -16
  110. package/{standards → claude/skills/session-map/references}/session.md +17 -11
  111. package/claude/skills/session-relay/REQUIREMENT.md +1 -1
  112. package/claude/skills/session-resume/REQUIREMENT.md +4 -6
  113. package/claude/skills/session-resume/SKILL.md +3 -9
  114. package/claude/skills/session-worktree/SKILL.md +1 -1
  115. package/claude/skills/session-worktree/references/main-root-writes.md +2 -2
  116. package/claude/skills/systematic-debugging/REQUIREMENT.md +0 -1
  117. package/claude/skills/target-check/REQUIREMENT.md +3 -3
  118. package/claude/skills/target-check/SKILL.md +1 -1
  119. package/claude/skills/target-check/references/domains.md +1 -1
  120. package/claude/skills/target-setup/REQUIREMENT.md +4 -4
  121. package/claude/skills/target-setup/SKILL.md +3 -3
  122. package/claude/skills/target-setup/references/detect.md +1 -1
  123. package/claude/skills/target-setup/references/indexes.md +1 -1
  124. package/claude/skills/task-board/SKILL.md +4 -4
  125. package/claude/skills/task-board/references/archive.md +1 -1
  126. package/{standards → claude/skills/task-board/references}/board.md +9 -0
  127. package/claude/skills/task-board/references/decline.md +1 -1
  128. package/{standards → claude/skills/task-board/references}/tasks.md +13 -8
  129. package/claude/skills/teach-workspace/SKILL.md +1 -1
  130. package/claude/skills/teach-workspace/references/revisit.md +1 -1
  131. package/{standards → claude/skills/teach-workspace/references}/teach.md +17 -5
  132. package/claude/skills/test-first/REQUIREMENT.md +1 -1
  133. package/claude/skills/ux-audit/REQUIREMENT.md +2 -2
  134. package/claude/skills/ux-audit/SKILL.md +2 -3
  135. package/claude/skills/write-human/REQUIREMENT.md +0 -1
  136. package/claude/skills/write-human/SKILL.md +2 -2
  137. package/docs/agents/audits.md +1 -1
  138. package/docs/agents/canvas.md +1 -1
  139. package/docs/agents/commands.md +6 -10
  140. package/docs/agents/context-audit-checks.md +10 -24
  141. package/docs/agents/context-audit.md +7 -15
  142. package/docs/agents/context-classify.md +5 -5
  143. package/docs/agents/counts.md +5 -5
  144. package/docs/agents/drift-surfaces.md +2 -2
  145. package/docs/agents/index.md +3 -4
  146. package/docs/agents/install-and-sync.md +6 -6
  147. package/docs/agents/intake.md +1 -1
  148. package/docs/agents/markdown-audit.md +1 -1
  149. package/docs/agents/records-stale.md +5 -5
  150. package/docs/agents/records-validate.md +1 -1
  151. package/docs/agents/records.md +2 -2
  152. package/docs/agents/restated.md +1 -1
  153. package/docs/agents/routing.md +1 -1
  154. package/docs/agents/rule-citations.md +1 -1
  155. package/docs/agents/skills-audit.md +2 -2
  156. package/docs/agents/state-scoped-risk.md +2 -2
  157. package/docs/agents/superseded.md +2 -2
  158. package/docs/agents/tasks-plans.md +2 -2
  159. package/docs/agents/tasks-validate.md +1 -1
  160. package/docs/agents/tasks.md +1 -1
  161. package/docs/agents/teach.md +1 -1
  162. package/docs/agents/test-order.md +1 -1
  163. package/docs/agents/upstream.md +94 -0
  164. package/docs/target/migrations.md +24 -0
  165. package/docs/target/projects.md +5 -7
  166. package/docs/target/scenarios.md +2 -2
  167. package/docs/target/sync.md +3 -3
  168. package/docs/workflow/ai-workflow.md +7 -38
  169. package/docs/workflow/index.md +1 -1
  170. package/docs/workflow/operating-model.md +8 -8
  171. package/docs/workflow/parallel-features.md +7 -7
  172. package/docs/workflow/skill-map-on-request.md +13 -16
  173. package/docs/workflow/skill-map.md +20 -32
  174. package/docs/workflow/visual-design-workflow.md +11 -14
  175. package/governance/renames.toml +0 -23
  176. package/governance/rules/canon/605-worktrees.md +1 -1
  177. package/governance/rules/{standards → canon}/plan.md +5 -5
  178. package/governance/rules/canon/tasks.md +18 -0
  179. package/governance/rules/writing/markdown.md +23 -0
  180. package/governance/stacks/base.toml +1 -1
  181. package/package.json +1 -1
  182. package/scripts/core/install-check.sh +2 -2
  183. package/scripts/core/regen-web-previews.ts +1 -1
  184. package/src/audits/catalog.ts +5 -97
  185. package/src/claude/cases/authoring.ts +5 -18
  186. package/src/claude/cases/misc.ts +0 -17
  187. package/src/claude/cases/setup.ts +3 -20
  188. package/src/claude/cases/workflow.ts +0 -33
  189. package/src/claude/seeds.ts +1 -7
  190. package/src/claude/skills-audit.ts +1 -1
  191. package/src/claude/skills-reach.ts +1 -1
  192. package/src/cli/help.ts +3 -4
  193. package/src/cli.ts +2 -4
  194. package/src/commands/claude.ts +9 -15
  195. package/src/commands/context.ts +18 -280
  196. package/src/commands/feedback.ts +22 -91
  197. package/src/commands/gov.ts +2 -2
  198. package/src/commands/records.ts +24 -6
  199. package/src/commands/sync.ts +5 -3
  200. package/src/commands/upstream.ts +430 -0
  201. package/src/context/audit.ts +6 -15
  202. package/src/context/classify/extract.ts +6 -13
  203. package/src/context/classify/patterns.ts +2 -41
  204. package/src/context/classify/prompts.ts +2 -6
  205. package/src/context/classify/run.ts +3 -3
  206. package/src/context/folders.ts +7 -12
  207. package/src/context/gate.ts +11 -91
  208. package/src/context/requirements.ts +13 -4
  209. package/src/counts/scan.ts +3 -3
  210. package/src/design/components.ts +2 -2
  211. package/src/feedback/body.ts +0 -12
  212. package/src/gate/measures.ts +6 -57
  213. package/src/gate/stages.ts +0 -1
  214. package/src/gov/citations.ts +9 -3
  215. package/src/gov/restated.ts +3 -3
  216. package/src/markdown/structure.ts +3 -3
  217. package/src/migrate/record-layout.ts +3 -3
  218. package/src/records/backup.ts +1 -0
  219. package/src/records/ordinal.ts +1 -1
  220. package/src/records/prune.ts +2 -1
  221. package/src/records/stale-canonical.ts +2 -2
  222. package/src/records/validate.ts +55 -0
  223. package/src/roots/record.ts +2 -1
  224. package/src/sandbox/scenario.ts +115 -11
  225. package/src/shipped/references.ts +3 -3
  226. package/src/standards/read.ts +52 -1
  227. package/src/sync/seeds-report.ts +1 -1
  228. package/src/tasks/answers.ts +14 -1
  229. package/src/tasks/reach.ts +2 -2
  230. package/src/tasks/stack.ts +176 -0
  231. package/src/tasks/validate.ts +1 -1
  232. package/src/teach/workspace.ts +1 -1
  233. package/src/upstream/catalog.ts +112 -0
  234. package/src/upstream/cursor.ts +120 -0
  235. package/src/upstream/digest.ts +75 -0
  236. package/src/upstream/due.ts +128 -0
  237. package/src/upstream/releases.ts +170 -0
  238. package/standards/context.md +3 -5
  239. package/standards/design.md +2 -3
  240. package/standards/index.md +0 -23
  241. package/standards/publish.md +1 -1
  242. package/standards/rule.md +1 -1
  243. package/standards/skill.md +1 -1
  244. package/tooling/base/reference.md +1 -1
  245. package/tooling/claude/reference.md +6 -20
  246. package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +1 -1
  247. package/tooling/claude/seeds/.claude/hooks/records-index.sh +8 -20
  248. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +3 -3
  249. package/tooling/claude/seeds/CLAUDE.md +1 -3
  250. package/tooling/claude/seeds/canon/REQUIREMENTS.md +2 -9
  251. package/tooling/web/configs/e2e/screenshot.ts +0 -1
  252. package/claude/skills/auto-ship/references/review-findings.md +0 -20
  253. package/claude/skills/bash-cli-script/REQUIREMENT.md +0 -43
  254. package/claude/skills/bash-cli-script/SKILL.md +0 -53
  255. package/claude/skills/bash-cli-script/references/template.md +0 -19
  256. package/claude/skills/canon-operator/REQUIREMENT.md +0 -62
  257. package/claude/skills/canon-operator/SKILL.md +0 -113
  258. package/claude/skills/canon-rollout/REQUIREMENT.md +0 -59
  259. package/claude/skills/canon-rollout/SKILL.md +0 -140
  260. package/claude/skills/canon-rollout/references/failure-recovery.md +0 -20
  261. package/claude/skills/context-fold/references/anchor-sweep.md +0 -58
  262. package/claude/skills/context-fold/references/wireframe-sweep.md +0 -45
  263. package/claude/skills/create-standard/REQUIREMENT.md +0 -33
  264. package/claude/skills/create-standard/SKILL.md +0 -31
  265. package/claude/skills/design-extract/REQUIREMENT.md +0 -42
  266. package/claude/skills/design-extract/SKILL.md +0 -108
  267. package/claude/skills/draft-and-pick/references/frame.html +0 -118
  268. package/claude/skills/draft-doc/references/wireframes.md +0 -48
  269. package/claude/skills/index-lookup/REQUIREMENT.md +0 -35
  270. package/claude/skills/index-lookup/SKILL.md +0 -44
  271. package/claude/skills/markdown-propose/REQUIREMENT.md +0 -47
  272. package/claude/skills/markdown-propose/SKILL.md +0 -122
  273. package/claude/skills/markdown-propose/references/format.md +0 -118
  274. package/claude/skills/memory-capture/REQUIREMENT.md +0 -50
  275. package/claude/skills/memory-capture/SKILL.md +0 -117
  276. package/claude/skills/memory-review/REQUIREMENT.md +0 -52
  277. package/claude/skills/memory-review/SKILL.md +0 -129
  278. package/claude/skills/memory-review/references/later-phases.md +0 -118
  279. package/claude/skills/memory-review/references/receipt-format.md +0 -64
  280. package/claude/skills/project-commands/REQUIREMENT.md +0 -42
  281. package/claude/skills/project-commands/SKILL.md +0 -87
  282. package/claude/skills/repo-metadata/REQUIREMENT.md +0 -37
  283. package/claude/skills/repo-metadata/SKILL.md +0 -54
  284. package/claude/skills/restate-plainly/REQUIREMENT.md +0 -41
  285. package/claude/skills/restate-plainly/SKILL.md +0 -41
  286. package/claude/skills/review-branch/REQUIREMENT.md +0 -40
  287. package/claude/skills/review-branch/SKILL.md +0 -137
  288. package/claude/skills/seed-sync/REQUIREMENT.md +0 -45
  289. package/claude/skills/seed-sync/SKILL.md +0 -158
  290. package/claude/skills/sketch-design/REQUIREMENT.md +0 -38
  291. package/claude/skills/sketch-design/SKILL.md +0 -94
  292. package/claude/skills/ux-walkthrough/REQUIREMENT.md +0 -56
  293. package/claude/skills/ux-walkthrough/SKILL.md +0 -53
  294. package/claude/skills/ux-walkthrough/references/builds.md +0 -15
  295. package/claude/skills/ux-walkthrough/references/candidate-pages.md +0 -11
  296. package/claude/skills/ux-walkthrough/references/measuring.md +0 -22
  297. package/claude/skills/ux-walkthrough/references/record.md +0 -36
  298. package/claude/skills/ux-walkthrough/references/relay.md +0 -25
  299. package/docs/agents/architecture-record.md +0 -36
  300. package/docs/agents/review-classification.md +0 -76
  301. package/governance/rules/canon/601-indexes.md +0 -9
  302. package/governance/rules/canon/603-memory.md +0 -11
  303. package/governance/rules/claude/565-behavior.md +0 -27
  304. package/governance/rules/claude/567-planning.md +0 -16
  305. package/governance/rules/claude/575-hooks.md +0 -16
  306. package/governance/rules/claude/576-settings.md +0 -13
  307. package/governance/rules/claude/592-claude-md.md +0 -19
  308. package/governance/rules/lang/120-bash.md +0 -18
  309. package/governance/rules/standards/architecture.md +0 -13
  310. package/governance/rules/standards/groundwork.md +0 -13
  311. package/governance/rules/standards/intake.md +0 -12
  312. package/governance/rules/standards/markdown.md +0 -16
  313. package/governance/rules/standards/memory.md +0 -12
  314. package/governance/rules/standards/ready.md +0 -13
  315. package/governance/rules/standards/requirements.md +0 -12
  316. package/governance/rules/standards/session.md +0 -13
  317. package/governance/rules/standards/tasks.md +0 -15
  318. package/governance/rules/standards/teach.md +0 -14
  319. package/governance/rules/standards/wiki.md +0 -13
  320. package/governance/rules/standards/wireframes.md +0 -13
  321. package/governance/rules/writing/800-prose.md +0 -15
  322. package/src/autoship/classify.ts +0 -75
  323. package/src/autoship/paths.ts +0 -50
  324. package/src/commands/autoship.ts +0 -129
  325. package/src/commands/repo.ts +0 -393
  326. package/src/context/architecture.ts +0 -654
  327. package/src/context/wireframe-states.ts +0 -238
  328. package/src/repo/metadata.ts +0 -206
  329. package/standards/architecture.md +0 -127
  330. package/standards/memory.md +0 -158
  331. package/standards/mermaid.md +0 -64
  332. package/standards/requirements.md +0 -79
  333. package/standards/wiki.md +0 -77
  334. package/standards/wireframes.md +0 -143
  335. package/tooling/claude/seeds/.claude/memory/index.md +0 -8
  336. package/tooling/claude/seeds/canon/ARCHITECTURE.md +0 -23
  337. package/tooling/claude/seeds/canon/wireframes/index.md +0 -8
  338. /package/{standards → claude/skills/create-skill/references}/skill-practice.md +0 -0
  339. /package/{standards → claude/skills/create-skill/references}/skill-requirement.md +0 -0
  340. /package/{standards → claude/skills/plan-feature/references}/plan-lifecycle.md +0 -0
  341. /package/{standards → claude/skills/plan-groundwork/references}/groundwork-spikes.md +0 -0
  342. /package/{standards → claude/skills/task-board/references}/tasks-decline.md +0 -0
  343. /package/{standards → claude/skills/teach-workspace/references}/glossary.md +0 -0
@@ -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
@@ -92,7 +92,7 @@ const SENTENCE_END = /[.!?]["'’”)\]]*(?=\s+(?:["'“(\[]*[A-Z]|`)|\s*$)/g
92
92
  * Checkpoints the structural measures run against.
93
93
  *
94
94
  * These are the definition rather than a fallback. Reading each number out of
95
- * the sentence `standards/markdown.md` states it in was the alternative, and it
95
+ * the sentence `claude/skills/markdown-craft/references/markdown.md` states it in was the alternative, and it
96
96
  * put a parser contract on a document authored for people, where a rewording
97
97
  * degraded a number and the report had to carry a legend saying which one. The
98
98
  * standard still states every number for a reader, and moving one is an edit to
@@ -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
 
@@ -47,6 +47,7 @@ export const BACKED_FOLDERS = [
47
47
  'tasks',
48
48
  'teach',
49
49
  'transcripts',
50
+ 'upstream',
50
51
  'walkthroughs',
51
52
  ] as const
52
53
 
@@ -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. */
@@ -75,6 +75,8 @@ export const FINDING_KINDS = [
75
75
  'category-mismatch',
76
76
  'operator-call-phrasing',
77
77
  'batch-unsplit',
78
+ 'source-unquoted',
79
+ 'stack-foldable',
78
80
  ] as const
79
81
 
80
82
  export type FindingKind = (typeof FINDING_KINDS)[number]
@@ -276,6 +278,7 @@ const PLAN_SECTIONS = [
276
278
  'Risks',
277
279
  'Review focus',
278
280
  'Questions',
281
+ 'Sources',
279
282
  ] as const
280
283
 
281
284
  type PlanSection = (typeof PLAN_SECTIONS)[number]
@@ -425,6 +428,57 @@ function checkQuestionContract(name: string, lines: string[]): Finding[] {
425
428
  return findings
426
429
  }
427
430
 
431
+ const SOURCE_LINK = /\[[^\]]+\]\(https?:\/\/[^)\s]+\)|https?:\/\/\S+/
432
+ const SOURCE_QUOTE = /["“][^"“”]+["”]/
433
+ const SOURCE_UNVERIFIED = /\bunverified\b/i
434
+
435
+ /**
436
+ * A bullet opens an entry only at the section's shallowest indent, so a nested
437
+ * bullet and a wrapped line both belong to the entry above. Joining them first
438
+ * lets a passage wrap across lines and still count as quoted.
439
+ */
440
+ function readSourceEntries(lines: readonly string[]): string[] {
441
+ const entries: string[] = []
442
+ let depth: number | undefined
443
+
444
+ for (const line of lines) {
445
+ const trimmed = line.trim()
446
+ if (trimmed === '') continue
447
+
448
+ const indent = line.length - line.trimStart().length
449
+ const opensEntry =
450
+ trimmed.startsWith('- ') && (depth === undefined || indent <= depth)
451
+
452
+ if (opensEntry) {
453
+ depth = indent
454
+ entries.push(trimmed)
455
+ continue
456
+ }
457
+
458
+ if (entries.length > 0) entries[entries.length - 1] += ` ${trimmed}`
459
+ }
460
+
461
+ return entries
462
+ }
463
+
464
+ function checkSources(name: string, lines: readonly string[]): Finding[] {
465
+ return readSourceEntries(lines)
466
+ .filter((entry) => entry !== `- ${NONE_IDENTIFIED}`)
467
+ .filter(
468
+ (entry) =>
469
+ !SOURCE_LINK.test(entry) ||
470
+ !(SOURCE_QUOTE.test(entry) || SOURCE_UNVERIFIED.test(entry)),
471
+ )
472
+ .map((entry) =>
473
+ finding(
474
+ 'source-unquoted',
475
+ name,
476
+ shorten(entry),
477
+ 'carries no link, or a link with neither a double-quoted passage nor the word unverified.',
478
+ ),
479
+ )
480
+ }
481
+
428
482
  export function checkPlan(name: string, text: string): Finding[] {
429
483
  const findings: Finding[] = []
430
484
 
@@ -480,6 +534,7 @@ export function checkPlan(name: string, text: string): Finding[] {
480
534
  }
481
535
 
482
536
  findings.push(...checkQuestionContract(name, sections.get('Questions') ?? []))
537
+ findings.push(...checkSources(name, sections.get('Sources') ?? []))
483
538
 
484
539
  if (hasStagedBatches(text)) {
485
540
  const staged = linesOutsideFences(text).find((line) =>
@@ -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
  */
@@ -80,6 +80,7 @@ export const RECORD_ENTRIES: readonly string[] = [
80
80
  'tasks',
81
81
  'teach',
82
82
  'transcripts',
83
+ 'upstream',
83
84
  'walkthroughs',
84
85
  ]
85
86
 
@@ -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
@@ -53,6 +53,46 @@ export function standardRoots(root: string): StandardRoot[] {
53
53
  ]
54
54
  }
55
55
 
56
+ /**
57
+ * Standards retired into the skill reference that took them, keyed by name with
58
+ * a path relative to the package root. Each retirement adds one line, and the
59
+ * map goes with the verb in the last slice of the retirement.
60
+ */
61
+ const MOVED_STANDARDS: Readonly<Record<string, string>> = {
62
+ board: 'claude/skills/task-board/references/board.md',
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]
94
+ }
95
+
56
96
  function filename(name: string): string {
57
97
  return name.endsWith('.md') ? name : `${name}.md`
58
98
  }
@@ -69,7 +109,16 @@ export function resolveStandard(
69
109
  if (existsSync(path)) return { path, source: join(label, file) }
70
110
  }
71
111
 
72
- return undefined
112
+ const movedName = basename(file, '.md')
113
+ if (!Object.hasOwn(MOVED_STANDARDS, movedName)) return undefined
114
+
115
+ const moved = MOVED_STANDARDS[movedName]
116
+ if (!moved) return undefined
117
+
118
+ const path = join(PROJECT_ROOT, moved)
119
+ if (!existsSync(path)) return undefined
120
+
121
+ return { path, source: join(PACKAGE_LABEL, moved) }
73
122
  }
74
123
 
75
124
  /**
@@ -92,6 +141,8 @@ export function listStandards(root: string): string[] {
92
141
  }
93
142
  }
94
143
 
144
+ for (const name of Object.keys(MOVED_STANDARDS)) names.add(name)
145
+
95
146
  return [...names].sort()
96
147
  }
97
148
 
@@ -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.
@@ -1,7 +1,8 @@
1
1
  import { existsSync } from 'node:fs'
2
2
  import { readFile } from 'node:fs/promises'
3
- import { isAbsolute, relative, resolve } from 'node:path'
3
+ import { basename, isAbsolute, relative, resolve } from 'node:path'
4
4
  import { isUnder } from '@/tasks/paths'
5
+ import { JUDGED_APART_REMEDY, readLivePlans, stackPairs } from '@/tasks/stack'
5
6
  import { recordDir, recordDirs } from '@/roots/record'
6
7
  import {
7
8
  hasStagedBatches,
@@ -231,6 +232,18 @@ export async function planAnswers(
231
232
  })
232
233
  }
233
234
 
235
+ const stem = basename(resolved.path, '.md')
236
+ const pairs = stackPairs(await readLivePlans(root))
237
+
238
+ for (const { child, parent, shared } of pairs) {
239
+ if (child !== stem) continue
240
+
241
+ open.push({
242
+ label: 'Stacked sibling',
243
+ why: `stacks on \`${parent}\` and shares ${shared} declared files with it. ${JUDGED_APART_REMEDY}`,
244
+ })
245
+ }
246
+
234
247
  return {
235
248
  ok: true,
236
249
  plan: resolved.plan,
@@ -11,7 +11,7 @@ const PLANS = 'plans'
11
11
  const MARKDOWN = '.md'
12
12
  const NONE_IDENTIFIED = 'None identified.'
13
13
 
14
- /** The one group a dispatch reads, per `standards/board.md`. */
14
+ /** The one group a dispatch reads, per `claude/skills/task-board/references/board.md`. */
15
15
  const DISPATCH_GROUP = 'Run now'
16
16
 
17
17
  export const REACH_REFUSALS = [
@@ -146,7 +146,7 @@ function namesFile(span: string): boolean {
146
146
  * An entry carrying no colon at all is taken whole. The standard requires a
147
147
  * reason rather than the punctuation introducing it, so the alternative is
148
148
  * reading such an entry as declaring nothing, which reports every path it
149
- * names as undeclared. `standards/plan.md` fixes the colon form, so the
149
+ * names as undeclared. `claude/skills/plan-feature/references/plan.md` fixes the colon form, so the
150
150
  * conforming entry never reaches this fallback.
151
151
  *
152
152
  * A rename declares both sides, since both are paths the branch writes and