@erclx/canon 5.26.0 → 5.28.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 (320) hide show
  1. package/README.md +2 -2
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/SKILL.md +2 -0
  4. package/claude/skills/auto-ship/SKILL.md +3 -11
  5. package/claude/skills/auto-ship/references/review-findings.md +20 -0
  6. package/claude/skills/backlog-triage/SKILL.md +2 -0
  7. package/claude/skills/bash-cli-script/SKILL.md +2 -0
  8. package/claude/skills/build-in-slices/SKILL.md +2 -0
  9. package/claude/skills/canon-cli/SKILL.md +2 -0
  10. package/claude/skills/canon-feedback/SKILL.md +2 -0
  11. package/claude/skills/canon-feedback-triage/SKILL.md +2 -0
  12. package/claude/skills/canon-operator/SKILL.md +2 -0
  13. package/claude/skills/canon-rollout/SKILL.md +3 -11
  14. package/claude/skills/canon-rollout/references/failure-recovery.md +20 -0
  15. package/claude/skills/canvas/SKILL.md +13 -0
  16. package/claude/skills/ci-workflow/SKILL.md +2 -0
  17. package/claude/skills/code-craft/SKILL.md +2 -0
  18. package/claude/skills/codebase-layout/SKILL.md +2 -0
  19. package/claude/skills/context-fold/SKILL.md +3 -17
  20. package/claude/skills/context-fold/references/completion-report.md +26 -0
  21. package/claude/skills/create-rule/SKILL.md +2 -0
  22. package/claude/skills/create-skill/SKILL.md +2 -0
  23. package/claude/skills/create-standard/SKILL.md +2 -0
  24. package/claude/skills/decision-escalate/SKILL.md +2 -0
  25. package/claude/skills/deploy-app/SKILL.md +2 -0
  26. package/claude/skills/deprecation-migration/SKILL.md +2 -0
  27. package/claude/skills/design-extract/SKILL.md +2 -0
  28. package/claude/skills/design-taste/SKILL.md +2 -0
  29. package/claude/skills/docs-sync/SKILL.md +2 -0
  30. package/claude/skills/document-health/SKILL.md +2 -0
  31. package/claude/skills/draft-and-pick/SKILL.md +2 -0
  32. package/claude/skills/draft-doc/SKILL.md +2 -0
  33. package/claude/skills/draft-doc/references/wiki.md +1 -1
  34. package/claude/skills/draft-figure/SKILL.md +2 -0
  35. package/claude/skills/draft-identity/SKILL.md +2 -0
  36. package/claude/skills/draft-ready/SKILL.md +5 -1
  37. package/claude/skills/draft-screencast/SKILL.md +2 -0
  38. package/claude/skills/draft-slides/SKILL.md +2 -0
  39. package/claude/skills/git-branch/SKILL.md +2 -0
  40. package/claude/skills/git-commit/SKILL.md +2 -0
  41. package/claude/skills/git-followup/SKILL.md +2 -0
  42. package/claude/skills/git-issue/SKILL.md +2 -0
  43. package/claude/skills/git-pr/SKILL.md +2 -0
  44. package/claude/skills/git-pr/references/evidence.md +7 -7
  45. package/claude/skills/git-ship/SKILL.md +2 -0
  46. package/claude/skills/git-split/SKILL.md +2 -0
  47. package/claude/skills/git-stage/SKILL.md +2 -0
  48. package/claude/skills/git-worktree/SKILL.md +2 -0
  49. package/claude/skills/index-lookup/SKILL.md +2 -0
  50. package/claude/skills/markdown-propose/SKILL.md +2 -0
  51. package/claude/skills/memory-capture/SKILL.md +2 -0
  52. package/claude/skills/memory-review/SKILL.md +2 -0
  53. package/claude/skills/plan-feature/SKILL.md +2 -0
  54. package/claude/skills/plan-groundwork/SKILL.md +2 -0
  55. package/claude/skills/plan-intake/SKILL.md +2 -0
  56. package/claude/skills/plan-intake-answer/SKILL.md +2 -0
  57. package/claude/skills/project-commands/SKILL.md +2 -0
  58. package/claude/skills/read-frames/SKILL.md +2 -0
  59. package/claude/skills/record-screencast/SKILL.md +2 -0
  60. package/claude/skills/repo-metadata/SKILL.md +2 -0
  61. package/claude/skills/restate-plainly/SKILL.md +2 -0
  62. package/claude/skills/review-address/REQUIREMENT.md +4 -2
  63. package/claude/skills/review-address/SKILL.md +19 -14
  64. package/claude/skills/review-address/references/post-review-findings.md +16 -0
  65. package/claude/skills/review-address/references/rebase-conflicts.md +4 -2
  66. package/claude/skills/review-branch/SKILL.md +2 -0
  67. package/claude/skills/review-craft/SKILL.md +2 -0
  68. package/claude/skills/review-craft/references/ui.md +6 -6
  69. package/claude/skills/review-pr/SKILL.md +4 -8
  70. package/claude/skills/review-pr/references/body-variants.md +52 -0
  71. package/claude/skills/review-pr/references/key-changes.md +16 -0
  72. package/claude/skills/review-pr/references/post.md +15 -43
  73. package/claude/skills/review-ui/REQUIREMENT.md +2 -2
  74. package/claude/skills/review-ui/SKILL.md +16 -12
  75. package/claude/skills/review-ui/references/driving.md +8 -8
  76. package/claude/skills/review-ui/references/post.md +13 -3
  77. package/claude/skills/review-ui/references/tick.md +22 -0
  78. package/claude/skills/role-orchestrator/REQUIREMENT.md +15 -15
  79. package/claude/skills/role-orchestrator/SKILL.md +31 -42
  80. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +18 -16
  81. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +13 -3
  82. package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +23 -0
  83. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +6 -34
  84. package/claude/skills/role-orchestrator/references/orchestrator-output.md +4 -4
  85. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +5 -5
  86. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +12 -23
  87. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +6 -6
  88. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +1 -1
  89. package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +38 -0
  90. package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +2 -2
  91. package/claude/skills/role-orchestrator/references/orchestrator-watch.md +26 -0
  92. package/claude/skills/role-orchestrator/scripts/poll.ts +304 -10
  93. package/claude/skills/role-planner/REQUIREMENT.md +1 -1
  94. package/claude/skills/role-planner/SKILL.md +4 -2
  95. package/claude/skills/role-reviewer/REQUIREMENT.md +1 -1
  96. package/claude/skills/role-reviewer/SKILL.md +3 -1
  97. package/claude/skills/role-worker/SKILL.md +5 -2
  98. package/claude/skills/search-craft/SKILL.md +2 -0
  99. package/claude/skills/seed-sync/SKILL.md +2 -0
  100. package/claude/skills/session-compact/SKILL.md +2 -0
  101. package/claude/skills/session-map/SKILL.md +2 -0
  102. package/claude/skills/session-relay/SKILL.md +2 -0
  103. package/claude/skills/session-resume/SKILL.md +2 -0
  104. package/claude/skills/session-worktree/SKILL.md +3 -11
  105. package/claude/skills/session-worktree/references/install-command.md +20 -0
  106. package/claude/skills/sketch-design/SKILL.md +2 -0
  107. package/claude/skills/systematic-debugging/SKILL.md +2 -0
  108. package/claude/skills/target-check/SKILL.md +2 -0
  109. package/claude/skills/target-setup/SKILL.md +2 -0
  110. package/claude/skills/task-board/SKILL.md +2 -0
  111. package/claude/skills/teach-workspace/SKILL.md +5 -15
  112. package/claude/skills/teach-workspace/references/lesson-build.md +75 -0
  113. package/claude/skills/teach-workspace/references/lesson-craft.md +13 -69
  114. package/claude/skills/teach-workspace/references/revisit.md +22 -0
  115. package/claude/skills/test-craft/SKILL.md +2 -0
  116. package/claude/skills/test-first/SKILL.md +2 -0
  117. package/claude/skills/ui-checklist/SKILL.md +3 -1
  118. package/claude/skills/ux-audit/SKILL.md +2 -0
  119. package/claude/skills/ux-measure/SKILL.md +2 -0
  120. package/claude/skills/ux-walkthrough/SKILL.md +2 -0
  121. package/claude/skills/video-craft/SKILL.md +2 -0
  122. package/claude/skills/write-human/SKILL.md +2 -0
  123. package/claude/skills/youtube-transcripts/SKILL.md +2 -0
  124. package/docs/agents/canvas.md +24 -8
  125. package/docs/agents/capture.md +5 -5
  126. package/docs/agents/commands.md +2 -2
  127. package/docs/agents/gate.md +3 -1
  128. package/docs/agents/index.md +5 -4
  129. package/docs/agents/install-and-sync.md +7 -0
  130. package/docs/agents/key-changes.md +1 -1
  131. package/docs/agents/pr-evidence.md +47 -23
  132. package/docs/agents/pr-local.md +19 -13
  133. package/docs/agents/pr-preview.md +44 -21
  134. package/docs/agents/pr-tick.md +72 -0
  135. package/docs/agents/sandbox.md +30 -6
  136. package/docs/agents/scripting.md +8 -1
  137. package/docs/agents/skills-audit.md +2 -1
  138. package/docs/agents/tasks-plans.md +1 -1
  139. package/docs/agents/tasks-validate.md +18 -18
  140. package/docs/agents/tasks.md +1 -1
  141. package/docs/workflow/operating-model.md +15 -21
  142. package/docs/workflow/parallel-features.md +24 -3
  143. package/docs/workflow/skill-map.md +3 -1
  144. package/docs/workflow/zshrc-aliases.md +1 -1
  145. package/package.json +1 -1
  146. package/scripts/core/regen-hero.ts +1 -1
  147. package/scripts/lib/sandbox-path.sh +6 -162
  148. package/src/audits/baseline.ts +1 -1
  149. package/src/audits/run.ts +2 -2
  150. package/src/canvas/address.ts +12 -3
  151. package/src/canvas/capture.ts +1 -1
  152. package/src/canvas/client/inspector/align-grid.tsx +162 -0
  153. package/src/canvas/client/inspector/color-field.tsx +201 -171
  154. package/src/canvas/client/inspector/color-inputs.tsx +139 -0
  155. package/src/canvas/client/inspector/color-picker.tsx +668 -0
  156. package/src/canvas/client/inspector/color-space.ts +240 -0
  157. package/src/canvas/client/inspector/field.tsx +66 -3
  158. package/src/canvas/client/inspector/fields.css +473 -6
  159. package/src/canvas/client/inspector/size-field.tsx +69 -0
  160. package/src/canvas/client/inspector/token-select.tsx +109 -0
  161. package/src/canvas/client/inspector/values.ts +117 -3
  162. package/src/canvas/client/inspector.tsx +311 -15
  163. package/src/canvas/client/keys.ts +54 -0
  164. package/src/canvas/client/selection.tsx +199 -0
  165. package/src/canvas/client/shell.css +210 -12
  166. package/src/canvas/client/state.ts +165 -14
  167. package/src/canvas/client/surface.tsx +341 -39
  168. package/src/canvas/client/tools.tsx +64 -0
  169. package/src/canvas/content.ts +51 -2
  170. package/src/canvas/edit.ts +49 -6
  171. package/src/canvas/editing.ts +203 -0
  172. package/src/canvas/global.d.ts +15 -0
  173. package/src/canvas/server.ts +149 -12
  174. package/src/canvas/tokens.ts +60 -18
  175. package/src/census/count.ts +1 -1
  176. package/src/claude/seeds.ts +3 -3
  177. package/src/claude/skills-audit.ts +28 -0
  178. package/src/claude/skills-drift.ts +1 -1
  179. package/src/claude/skills-families.ts +36 -0
  180. package/src/claude/skills-list.ts +76 -15
  181. package/src/claude/skills-reach.ts +1 -1
  182. package/src/commands/audits.ts +2 -2
  183. package/src/commands/canvas.ts +180 -3
  184. package/src/commands/claude.ts +70 -12
  185. package/src/commands/design.ts +145 -5
  186. package/src/commands/docs.ts +1 -1
  187. package/src/commands/feedback.ts +2 -2
  188. package/src/commands/gate.ts +9 -2
  189. package/src/commands/gov.ts +3 -3
  190. package/src/commands/hooks.ts +1 -1
  191. package/src/commands/indexes.ts +1 -1
  192. package/src/commands/init.ts +2 -2
  193. package/src/commands/intake.ts +1 -1
  194. package/src/commands/migrate.ts +2 -2
  195. package/src/commands/pr.ts +288 -65
  196. package/src/commands/records.ts +1 -1
  197. package/src/commands/repo.ts +1 -1
  198. package/src/commands/sandbox.ts +64 -15
  199. package/src/commands/sessions.ts +1 -1
  200. package/src/commands/slides.ts +1 -1
  201. package/src/commands/sync.ts +2 -2
  202. package/src/commands/tasks.ts +21 -4
  203. package/src/commands/teach.ts +1 -1
  204. package/src/commands/tooling.ts +1 -1
  205. package/src/commands/transcripts.ts +2 -2
  206. package/src/commands/upgrade.ts +1 -1
  207. package/src/commands/wiki.ts +1 -1
  208. package/src/commands/worktrees.ts +1 -1
  209. package/src/comments/scan.ts +1 -1
  210. package/src/comments/trend.ts +1 -1
  211. package/src/context/architecture.ts +1 -1
  212. package/src/context/citations.ts +3 -3
  213. package/src/context/classify/extract.ts +3 -3
  214. package/src/context/folders.ts +2 -2
  215. package/src/context/requirements.ts +1 -1
  216. package/src/design/project-fonts.ts +392 -0
  217. package/src/docs/read.ts +1 -1
  218. package/src/exec.ts +1 -1
  219. package/src/feedback/github.ts +2 -2
  220. package/src/gate/measures.ts +48 -10
  221. package/src/gate/sequencer.ts +56 -4
  222. package/src/gate/stages.ts +17 -0
  223. package/src/gate/suite-lock.ts +214 -0
  224. package/src/{git-files.ts → git/files.ts} +2 -2
  225. package/src/{git-ignore.ts → git/ignore.ts} +1 -1
  226. package/src/{worktree.ts → git/worktree.ts} +1 -1
  227. package/src/gov/citations.ts +1 -1
  228. package/src/gov/install.ts +1 -1
  229. package/src/gov/superseded.ts +1 -1
  230. package/src/gov/test-order.ts +2 -2
  231. package/src/help.ts +1 -1
  232. package/src/hooks/post-merge.ts +1 -1
  233. package/src/indexes/walk.ts +1 -1
  234. package/src/intake/folder.ts +1 -1
  235. package/src/labels/audit.ts +1 -1
  236. package/src/labels/map.ts +1 -1
  237. package/src/labels/phase.ts +3 -3
  238. package/src/markdown/files.ts +1 -1
  239. package/src/migrate/apply.ts +1 -1
  240. package/src/migrate/record-layout.ts +1 -1
  241. package/src/migrate/records.ts +11 -11
  242. package/src/migrate/scratch-evidence.ts +1 -1
  243. package/src/migrate/surface-roots.ts +6 -6
  244. package/src/pr/bijection.ts +1 -1
  245. package/src/pr/evidence.ts +250 -59
  246. package/src/pr/local.ts +16 -10
  247. package/src/pr/preview.ts +78 -2
  248. package/src/pr/tick.ts +56 -0
  249. package/src/process/harness.ts +1 -1
  250. package/src/records/backup.ts +2 -2
  251. package/src/records/ordinal.ts +1 -1
  252. package/src/records/prune.ts +1 -1
  253. package/src/records/size.ts +1 -1
  254. package/src/records/stale-canonical.ts +2 -2
  255. package/src/records/stale.ts +1 -1
  256. package/src/records/validate.ts +2 -2
  257. package/src/{project-root.ts → roots/project.ts} +1 -1
  258. package/src/{record-root.ts → roots/record.ts} +1 -1
  259. package/src/sandbox/coverage.ts +11 -12
  260. package/src/sandbox/equivalence.ts +57 -26
  261. package/src/sandbox/headless/dispatch.ts +264 -0
  262. package/src/sandbox/headless/run.ts +653 -0
  263. package/src/sandbox/headless/watch.ts +145 -0
  264. package/src/sandbox/hooks.ts +1 -1
  265. package/src/sandbox/provision.ts +90 -29
  266. package/src/sandbox/scenario.ts +588 -0
  267. package/src/sandbox/tree.ts +4 -8
  268. package/src/secrets/scan.ts +1 -1
  269. package/src/sessions/claim.ts +1 -1
  270. package/src/sessions/live.ts +4 -1
  271. package/src/sessions/resolve.ts +1 -1
  272. package/src/sessions/transfer/bundle.ts +2 -2
  273. package/src/slides/decks.ts +1 -1
  274. package/src/slides/package/fonts.ts +6 -2
  275. package/src/standards/audit.ts +2 -2
  276. package/src/standards/read.ts +1 -1
  277. package/src/sync/check.ts +2 -2
  278. package/src/sync/engine.ts +3 -3
  279. package/src/sync/git.ts +1 -1
  280. package/src/sync/history.ts +1 -1
  281. package/src/sync/layout.ts +2 -2
  282. package/src/sync/reverse.ts +1 -1
  283. package/src/sync/stamp.ts +1 -1
  284. package/src/sync/target.ts +1 -1
  285. package/src/sync/workflow.ts +1 -1
  286. package/src/targets/pulls.ts +2 -2
  287. package/src/targets/sweep.ts +2 -2
  288. package/src/{target.ts → targets/validate.ts} +1 -1
  289. package/src/tasks/answers.ts +1 -1
  290. package/src/tasks/archive.ts +1 -1
  291. package/src/tasks/label.ts +1 -1
  292. package/src/tasks/reach.ts +2 -2
  293. package/src/tasks/trunk.ts +1 -1
  294. package/src/tasks/validate.ts +38 -14
  295. package/src/teach/nav.ts +1 -1
  296. package/src/teach/workspace.ts +1 -1
  297. package/src/tooling/inject.ts +2 -2
  298. package/src/tooling/read.ts +1 -1
  299. package/src/tooling/scan.ts +1 -1
  300. package/src/tooling/subfolder.ts +1 -1
  301. package/src/version/installed.ts +1 -1
  302. package/src/version/skew.ts +1 -1
  303. package/src/web/readme-citations.ts +1 -1
  304. package/src/worktrees/bare-flag.ts +1 -1
  305. package/src/worktrees/reclaim.ts +2 -2
  306. package/src/worktrees/remove.ts +1 -1
  307. package/standards/board.md +3 -3
  308. package/standards/canvas.md +2 -1
  309. package/standards/plan.md +13 -7
  310. package/standards/skill.md +1 -0
  311. package/standards/wiki.md +12 -3
  312. package/tooling/base/configs/.github/workflows/pr-local-preview.yml +2 -2
  313. package/tsconfig.json +1 -1
  314. package/scripts/lib/sandbox-dispatch.sh +0 -190
  315. /package/src/{client-commands.ts → gate/client-commands.ts} +0 -0
  316. /package/src/{git-base.ts → git/base.ts} +0 -0
  317. /package/src/{git-env.ts → git/env.ts} +0 -0
  318. /package/src/{legacy-path.ts → roots/legacy.ts} +0 -0
  319. /package/src/{surface-root.ts → roots/surface.ts} +0 -0
  320. /package/src/{copy.ts → sync/copy.ts} +0 -0
@@ -1,7 +1,7 @@
1
- import { existsSync, readFileSync, readdirSync } from 'node:fs'
1
+ import { existsSync, readFileSync } from 'node:fs'
2
2
  import { join } from 'node:path'
3
3
  import type { Command } from 'commander'
4
- import { checkoutMismatchWarning, PROJECT_ROOT } from '@/project-root'
4
+ import { checkoutMismatchWarning, PROJECT_ROOT } from '@/roots/project'
5
5
  import {
6
6
  assertedPercent,
7
7
  collectCensus,
@@ -25,7 +25,9 @@ import {
25
25
  type RunEnvelope,
26
26
  type Verdict,
27
27
  } from '@/sandbox/expect'
28
+ import { runHeadless } from '@/sandbox/headless/run'
28
29
  import { runSandbox } from '@/sandbox/provision'
30
+ import { listCategories, listScenarioFiles } from '@/sandbox/scenario'
29
31
  import { sandboxTree } from '@/sandbox/tree'
30
32
  import {
31
33
  frameError,
@@ -70,9 +72,6 @@ function reportAbsentScenarioTree(): boolean {
70
72
  return true
71
73
  }
72
74
 
73
- /** Holds fixture content for scenarios rather than scenarios of its own. */
74
- const FIXTURES_DIR = 'fixtures'
75
-
76
75
  /**
77
76
  * The provision verb takes positional words rather than options, since `reset`
78
77
  * and `clean` share its slot with a target, so its usage is written out here
@@ -124,17 +123,16 @@ interface CoverageOptions {
124
123
  }
125
124
 
126
125
  function getCategories(): string[] {
127
- return readdirSync(SANDBOX_DIR, { withFileTypes: true })
128
- .filter((d) => d.isDirectory() && d.name !== FIXTURES_DIR)
129
- .map((d) => d.name)
130
- .sort()
126
+ return listCategories(SANDBOX_DIR)
131
127
  }
132
128
 
129
+ /** Lists a stem in both forms too, so picking it reaches the refusal that names it. */
133
130
  function getCommands(category: string): string[] {
134
- return readdirSync(join(SANDBOX_DIR, category), { withFileTypes: true })
135
- .filter((f) => f.isFile() && f.name.endsWith('.sh'))
136
- .map((f) => f.name.replace(/\.sh$/, ''))
137
- .sort()
131
+ const { scenarios, ambiguous } = listScenarioFiles(
132
+ join(SANDBOX_DIR, category),
133
+ )
134
+
135
+ return [...scenarios.map((s) => s.command), ...ambiguous].sort()
138
136
  }
139
137
 
140
138
  async function interactivePicker(): Promise<string> {
@@ -510,7 +508,7 @@ export function register(program: Command): void {
510
508
  if (mismatch !== undefined) logWarn(mismatch)
511
509
 
512
510
  // `--no-header` is the frame opt-out for a caller that already opened
513
- // one, which `sandbox/run.sh` and the picker below both do.
511
+ // one, which `canon sandbox run` and the picker below both do.
514
512
  const isHeader = cmd.args[0] !== '--no-header'
515
513
  const args = isHeader ? cmd.args : cmd.args.slice(1)
516
514
 
@@ -527,6 +525,57 @@ export function register(program: Command): void {
527
525
  })
528
526
  })
529
527
 
528
+ sandbox
529
+ .command('run')
530
+ .description(
531
+ 'Provision a scenario, drive one claude -p session over it, and print the envelope with its verdict',
532
+ )
533
+ .argument('[target]', 'Scenario as <category>:<command>, e.g. git:commit')
534
+ .argument('[prompt]', 'Skill invocation, e.g. "/canon:git-commit"')
535
+ .argument('[arm]', 'Named scenario arm, e.g. small')
536
+ .helpOption('-h, --help', 'Show this help message')
537
+ .addHelpText(
538
+ 'after',
539
+ [
540
+ '',
541
+ 'Env overrides:',
542
+ ' CANON_SKILL_TEST_MODEL default sonnet',
543
+ ' CANON_SKILL_TEST_TOOLS default Bash,Read,Glob,Grep,Edit,Write',
544
+ ' CANON_SKILL_TEST_MAX_TURNS default 30',
545
+ ' CANON_SKILL_TEST_PERMISSION_MODE default bypassPermissions',
546
+ '',
547
+ 'Examples:',
548
+ ' canon sandbox run git:commit "/canon:git-commit"',
549
+ ' canon sandbox run claude:plan-feature "/canon:plan-feature add a widget" small',
550
+ '',
551
+ 'Bills a real session through the claude binary on PATH.',
552
+ 'Exit codes: the verdict, 1 when the envelope does not parse, or the',
553
+ 'provisioning or session code when either stops the run first.',
554
+ ].join('\n'),
555
+ )
556
+ .action(
557
+ async (
558
+ target: string | undefined,
559
+ prompt: string | undefined,
560
+ arm: string | undefined,
561
+ _opts: unknown,
562
+ cmd: Command,
563
+ ) => {
564
+ if (target === undefined) {
565
+ cmd.help()
566
+ return
567
+ }
568
+ if (reportAbsentScenarioTree()) return
569
+
570
+ process.exitCode = await runHeadless({
571
+ projectRoot: PROJECT_ROOT,
572
+ target,
573
+ prompt,
574
+ arm,
575
+ })
576
+ },
577
+ )
578
+
530
579
  sandbox
531
580
  .command('check')
532
581
  .description('Check a provisioned sandbox against a scenario expectation')
@@ -628,7 +677,7 @@ export function register(program: Command): void {
628
677
  ' canon sandbox equivalence claude --json',
629
678
  ' canon sandbox equivalence claude:plan-feature --base origin/main',
630
679
  '',
631
- 'Provisions offline only and never runs sandbox/run.sh or a claude binary.',
680
+ 'Provisions offline only and never runs canon sandbox run or a claude binary.',
632
681
  '--stub-remote also runs the anchor arms, and wins over --include-anchor.',
633
682
  'Exit codes: 0 when every compared arm is identical or red the same way',
634
683
  'on both sides, 1 on any difference, exit mismatch, or enumeration error.',
@@ -24,7 +24,7 @@ import {
24
24
  pipeOutput,
25
25
  plural,
26
26
  } from '@/ui'
27
- import { mainWorktreeRoot } from '@/worktree'
27
+ import { mainWorktreeRoot } from '@/git/worktree'
28
28
 
29
29
  interface ListCommandOptions {
30
30
  readonly json?: boolean
@@ -1,7 +1,7 @@
1
1
  import { resolve } from 'node:path'
2
2
  import type { Command } from 'commander'
3
3
  import { INSTALL_BROWSER } from '@/browser/engine'
4
- import { creationRel, SCRATCH } from '@/record-root'
4
+ import { creationRel, SCRATCH } from '@/roots/record'
5
5
  import { listDecks, resolveDeck, SLIDES_FOLDER } from '@/slides/decks'
6
6
  import { openDeck } from '@/slides/open'
7
7
  import { intro, outro, palette } from '@/ui'
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs'
2
2
  import { join } from 'node:path'
3
3
  import type { Command } from 'commander'
4
4
  import { cliPath, cliRun } from '@/cli-run'
5
- import { PROJECT_ROOT } from '@/project-root'
5
+ import { PROJECT_ROOT } from '@/roots/project'
6
6
  import {
7
7
  buildCheckReport,
8
8
  type CheckReport,
@@ -20,7 +20,7 @@ import {
20
20
  } from '@/sync/target'
21
21
  import type { UnclaimedFolder } from '@/sync/reverse'
22
22
  import { runGitWorkflow } from '@/sync/workflow'
23
- import { resolveTarget } from '@/target'
23
+ import { resolveTarget } from '@/targets/validate'
24
24
  import {
25
25
  intro,
26
26
  isNonInteractive,
@@ -1,7 +1,7 @@
1
1
  import { relative } from 'node:path'
2
2
  import type { Command } from 'commander'
3
3
  import { execa } from 'execa'
4
- import { gitEnv } from '@/git-env'
4
+ import { gitEnv } from '@/git/env'
5
5
  import { type AnswersOutcome, planAnswers } from '@/tasks/answers'
6
6
  import { type BranchOutcome, planBranch } from '@/tasks/branch'
7
7
  import {
@@ -42,6 +42,7 @@ import {
42
42
  import {
43
43
  type Finding,
44
44
  type FolderClaim,
45
+ type SharedFiles,
45
46
  type WideToken,
46
47
  type Unplaced,
47
48
  type Untested,
@@ -59,7 +60,7 @@ import {
59
60
  outro,
60
61
  pipeOutput,
61
62
  } from '@/ui'
62
- import { mainWorktreeRoot } from '@/worktree'
63
+ import { mainWorktreeRoot } from '@/git/worktree'
63
64
 
64
65
  /** Returned when the board carries a finding, which is the gating result. */
65
66
  const EXIT_FINDINGS = 2
@@ -239,7 +240,8 @@ export function register(program: Command): void {
239
240
  ' no Needs a plan row has a live plan, and every Up next row has one',
240
241
  ' every task file carries a board row or a backlog line, never both',
241
242
  ' no task carries more than one row',
242
- ' no two Run now rows touch the same file',
243
+ ' every Run now row names the files it touches',
244
+ ' a file two Run now rows share is listed, never failed',
243
245
  '',
244
246
  'Exit codes:',
245
247
  ' 0 every check passed',
@@ -1383,7 +1385,7 @@ function reportValidation(
1383
1385
  logStep(outcome.findings.length === 0 ? 'Clean' : 'Findings')
1384
1386
  if (outcome.findings.length === 0) {
1385
1387
  logInfo(
1386
- 'every row resolves, every task sits on one surface, and each touches its own files',
1388
+ 'every row resolves, every task sits on one surface, and each run now row names its files',
1387
1389
  )
1388
1390
  } else {
1389
1391
  for (const finding of outcome.findings) logWarn(describe(finding))
@@ -1412,6 +1414,16 @@ function reportValidation(
1412
1414
  for (const claim of outcome.claims) logWarn(describeClaim(claim))
1413
1415
  }
1414
1416
 
1417
+ // A shared file holds nothing under the dispatch gate, so the pairs report
1418
+ // with the warn glyph and move no exit code. They stay listed for merge
1419
+ // order and for the holds a Touches cell cannot state.
1420
+ logStep('Shared files')
1421
+ if (outcome.shared.length === 0) {
1422
+ logInfo('no two run now rows name the same file')
1423
+ } else {
1424
+ for (const pair of outcome.shared) logWarn(describeShared(pair))
1425
+ }
1426
+
1415
1427
  // The width limit is a heuristic over one preview pane, so a wide token
1416
1428
  // reports and moves no exit code, and a measure failing on a cell that
1417
1429
  // fits would teach a reader to skip it.
@@ -1447,6 +1459,7 @@ function reportValidation(
1447
1459
  findings: outcome.findings,
1448
1460
  untested: outcome.untested,
1449
1461
  claims: outcome.claims,
1462
+ shared: outcome.shared,
1450
1463
  wide: outcome.wide,
1451
1464
  unplaced: outcome.unplaced,
1452
1465
  })}\n`,
@@ -1469,6 +1482,10 @@ function describeClaim(claim: FolderClaim): string {
1469
1482
  return `${claim.group}: ${claim.subject} ${claim.message}`
1470
1483
  }
1471
1484
 
1485
+ function describeShared(pair: SharedFiles): string {
1486
+ return `${pair.group}: ${pair.subject} ${pair.message}`
1487
+ }
1488
+
1472
1489
  function describeWide(token: WideToken): string {
1473
1490
  return `${token.group}: ${token.subject} ${token.message}`
1474
1491
  }
@@ -43,7 +43,7 @@ import {
43
43
  outro,
44
44
  pipeOutput,
45
45
  } from '@/ui'
46
- import { mainWorktreeRoot } from '@/worktree'
46
+ import { mainWorktreeRoot } from '@/git/worktree'
47
47
 
48
48
  /**
49
49
  * A pair as the command line spells it, split on the first `=` so a value
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path'
2
2
  import type { Command } from 'commander'
3
- import { checkoutMismatchWarning, PROJECT_ROOT } from '@/project-root'
3
+ import { checkoutMismatchWarning, PROJECT_ROOT } from '@/roots/project'
4
4
  import {
5
5
  injectConfigs,
6
6
  injectGitignore,
@@ -1,9 +1,9 @@
1
1
  import { resolve } from 'node:path'
2
2
  import type { Command } from 'commander'
3
- import { recordDir } from '@/record-root'
3
+ import { recordDir } from '@/roots/record'
4
4
  import { ensureYtDlp, fetchOne } from '@/transcripts/fetch'
5
5
  import { palette } from '@/ui'
6
- import { mainWorktreeRoot } from '@/worktree'
6
+ import { mainWorktreeRoot } from '@/git/worktree'
7
7
 
8
8
  interface TranscriptOptions {
9
9
  out?: string
@@ -1,6 +1,6 @@
1
1
  import type { Command } from 'commander'
2
2
  import { execa } from 'execa'
3
- import { findCheckoutMismatch, PROJECT_ROOT } from '@/project-root'
3
+ import { findCheckoutMismatch, PROJECT_ROOT } from '@/roots/project'
4
4
  import {
5
5
  frameError,
6
6
  intro,
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path'
2
2
  import type { Command } from 'commander'
3
- import { PROJECT_ROOT } from '@/project-root'
3
+ import { PROJECT_ROOT } from '@/roots/project'
4
4
  import {
5
5
  intro,
6
6
  logAdd,
@@ -16,7 +16,7 @@ import {
16
16
  type Unreadable,
17
17
  type WorktreeVerdict,
18
18
  } from '@/worktrees/reclaim'
19
- import { mainWorktreeRoot } from '@/worktree'
19
+ import { mainWorktreeRoot } from '@/git/worktree'
20
20
  import { repairBareFlag } from '@/worktrees/bare-flag'
21
21
  import {
22
22
  removeReclaimable,
@@ -1,6 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises'
2
2
  import { resolve } from 'node:path'
3
- import { listIgnored } from '@/git-ignore'
3
+ import { listIgnored } from '@/git/ignore'
4
4
 
5
5
  export type Language = 'ts' | 'sh'
6
6
 
@@ -1,5 +1,5 @@
1
1
  import { $ } from 'bun'
2
- import { gitEnv } from '@/git-env'
2
+ import { gitEnv } from '@/git/env'
3
3
  import {
4
4
  countFiles,
5
5
  isPruned,
@@ -2,7 +2,7 @@ import { access, readFile } from 'node:fs/promises'
2
2
  import { join, relative } from 'node:path'
3
3
  import { AUDITS } from '@/audits/catalog'
4
4
  import { type BodyLine, bodyLines } from '@/markdown/scan'
5
- import { surfaceDir } from '@/surface-root'
5
+ import { surfaceDir } from '@/roots/surface'
6
6
 
7
7
  /**
8
8
  * The record this measures, relative to `root`.
@@ -1,9 +1,9 @@
1
1
  import { existsSync } from 'node:fs'
2
2
  import { readFile } from 'node:fs/promises'
3
3
  import { resolve } from 'node:path'
4
- import { listRepositoryFiles } from '@/git-files'
5
- import { RECORD_ROOTS } from '@/record-root'
6
- import { SURFACE_ROOTS } from '@/surface-root'
4
+ import { listRepositoryFiles } from '@/git/files'
5
+ import { RECORD_ROOTS } from '@/roots/record'
6
+ import { SURFACE_ROOTS } from '@/roots/surface'
7
7
 
8
8
  /**
9
9
  * Suppresses citation checking for the source line carrying it.
@@ -2,9 +2,9 @@ import { existsSync, readFileSync } from 'node:fs'
2
2
  import { basename, relative, resolve } from 'node:path'
3
3
  import { $ } from 'bun'
4
4
  import { resolveFolders } from '@/context/folders'
5
- import { gitEnv } from '@/git-env'
6
- import { listChangedFiles, resolveBaseRef } from '@/git-files'
7
- import { surfaceDir } from '@/surface-root'
5
+ import { gitEnv } from '@/git/env'
6
+ import { listChangedFiles, resolveBaseRef } from '@/git/files'
7
+ import { surfaceDir } from '@/roots/surface'
8
8
 
9
9
  /**
10
10
  * The five canonical doc types the classifier reaches, matching the file
@@ -1,8 +1,8 @@
1
1
  import { existsSync } from 'node:fs'
2
2
  import { dirname, relative, resolve } from 'node:path'
3
3
  import { INDEX_FILE, listIndexes } from '@/indexes/walk'
4
- import { RECORD_ROOTS } from '@/record-root'
5
- import { SURFACE_ROOTS } from '@/surface-root'
4
+ import { RECORD_ROOTS } from '@/roots/record'
5
+ import { SURFACE_ROOTS } from '@/roots/surface'
6
6
 
7
7
  /**
8
8
  * Folder names under a record root audited by default.
@@ -2,7 +2,7 @@ import { readFile } from 'node:fs/promises'
2
2
  import { join, relative } from 'node:path'
3
3
  import { wordCount } from '@/context/architecture'
4
4
  import { bodyLines } from '@/markdown/scan'
5
- import { surfaceDir } from '@/surface-root'
5
+ import { surfaceDir } from '@/roots/surface'
6
6
 
7
7
  /**
8
8
  * The record this measures, relative to `root`, resolved at either surface