@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
@@ -10,7 +10,7 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
10
10
 
11
11
  - [Architecture record](architecture-record.md): The findings canon context audit reads off the architecture record, being the entry cap, revisit sentence, length, decision word, and risk bullet gates the record states for itself, the claim coverage report, and the word counts, plus the requirements record's word cap
12
12
  - [Audits](audits.md): Running every health check as one set, what the single verdict means, the exit code each outcome takes, the retained baseline and the delta it reports, and which corpora are kept out of the record
13
- - [Canvas](canvas.md): The canvas server, its content format, and the verbs that list, arrange, select, edit, and capture frames
13
+ - [Canvas](canvas.md): The canvas server, its content format, and the verbs that list, arrange, select, edit, mark, and capture frames
14
14
  - [Capture](capture.md): Rendering HTML sources to PNG, what the command asserts about fonts, and why the selector has no default
15
15
  - [Census](census.md): Tracked-plus-untracked file count, a breakdown by extension, and a line total that skips whatever reads as binary
16
16
  - [Command catalog](commands.md): Every top-level command with its purpose, each domain's subcommands and the page describing them, the browser commands, and the version skew report
@@ -37,9 +37,10 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
37
37
  - [Overview](overview.md): What this folder covers, the invocation rules every command inherits, and where domain behavior is documented instead
38
38
  - [The pull request evidence comparison](pr-evidence.md): What canon pr evidence compares, the marker that lets it edit its own comment rather than duplicate it, the refusal reasons it names, what the --check read reports as owed, and why the comparison anchors at the merge base rather than the previous push
39
39
  - [The pull request frame store](pr-frames.md): How canon pr frames pushes a UI review frame to a never-merged branch through the GitHub API and returns a branch-path link, which ref it writes, the pass segment that keeps an earlier image fixed, the refusal reasons it names, and how --drop and --prune rewrite the branch so a dropped frame leaves history
40
- - [The pull request local preview](pr-local.md): How canon pr local finds the server a worktree is running, why a server in another worktree is never reported, the refusal reasons it names, and how a close workflow removes the link with --remove
41
- - [The pull request preview deploy](pr-preview.md): How canon pr preview finds a fenced Cloudflare Pages deploy, dispatches it on a pull request's branch, reads the alias the run prints, the refusal reasons it names, and why an unfenced deploy is refused before anything runs
40
+ - [The pull request local preview](pr-local.md): How canon pr local finds the server a worktree is running, why a server in another worktree is never reported, the refusal reasons it names, and how a close workflow drops the address line with --remove
41
+ - [The pull request preview deploy](pr-preview.md): How canon pr preview finds a fenced Cloudflare Pages deploy, refuses a branch its push filter does not serve, dispatches it on a pull request's branch, reads the alias the run prints, the refusal reasons it names, and why an unfenced deploy is refused before anything runs
42
42
  - [Head-sensitive pull request reads](pr-reads.md): Resolving a branch tip from the remote rather than from the pull request object, reading what a review pass covered off its own marker rather than off GitHub's submission stamps, why an empty run list is not a pass, the refusal reasons each verb names, and what the remote read costs
43
+ - [The pull request checklist tick](pr-tick.md): How canon pr tick marks the checklist boxes a UI pass drove, the stamp that names the commit each passed at, why a render at a newer head clears a tick, the refusal reasons it names, and why a taste box is never ticked
43
44
  - [Records push](records-push.md): Backing the record folders to a private remote and restoring them, which folders the payload carries, the one-time setup, the refusal reasons, and the hook that runs the push
44
45
  - [Records stale](records-stale.md): Reading the memory pen as a review queue, what makes an entry due, how a cited path is read and resolved, the order entries come back in, counting the releases since the requirements and architecture records were last reviewed, and the exit codes and refusals
45
46
  - [Records validate](records-validate.md): Validating the session records and the standards corpus, the per-kind checks, which root each kind defaults to, the exit codes, and the refusal reasons
@@ -61,7 +62,7 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
61
62
  - [Targets](targets.md): The projects this toolkit installed into, the record the install writes against the sweep that backs it, what bounds each answer, and the cross-target pull request read
62
63
  - [Tasks archive](tasks-archive.md): Archiving a shipped task by stem or pull request, declining one decided against, the refusal reasons each carries, and how the plan and ready folder move with the task
63
64
  - [Tasks plans](tasks-plans.md): The five verbs that read or write a task's plan, being where it sits and who cites it, whether it waits on the operator, the branch it derives, what a branch reached against what it declared, and writing its Plan line
64
- - [Tasks validate](tasks-validate.md): Validating the board against the tree, the seven checks, the untested, claims, wide, and unplaced arrays, the exit codes, and the refusal reasons
65
+ - [Tasks validate](tasks-validate.md): Validating the board against the tree, the seven checks, the five report arrays, the exit codes, and the refusal reasons
65
66
  - [Tasks](tasks.md): Claiming the next phase label, recording a pull request number and closing an outcome, listing each task's readiness, and where archiving, the plan verbs, and board validation are described
66
67
  - [Teach authoring](teach-authoring.md): Resolving what the next lesson needs before it is written, rendering a lesson body's blocks to HTML, and rewriting the root listing, a contents page, and each lesson's chrome and quiz stepper from its marker regions
67
68
  - [Teach](teach.md): Listing learning workspaces with what their records schedule next, opening one with its required files, recording sources and glossary terms, viewing one with its chrome refreshed, where the lesson authoring verbs are described, the refusal reasons, and why every write here runs through a verb
@@ -57,6 +57,13 @@ touches, because that subfolder is project-authored by location the way
57
57
  `.claude/rules/project/` is. An override named exactly like the shipped file is
58
58
  still the project's.
59
59
 
60
+ A project's faces sit in that subfolder too, under `fonts/` with plain
61
+ `@font-face` rules in `fonts.css`. `canon design fonts add <file> --family <name>`
62
+ copies a `woff2`, `woff`, `ttf`, or `otf` face in and appends its rule
63
+ (`--weight`, `--style`, `--json`), refusing bytes that disagree with the
64
+ extension as `invalid-font`. `fonts list --json` emits one row per rule, with a
65
+ `problem` where the file is missing or lies outside the subfolder.
66
+
60
67
  The override ships absent rather than empty. An empty file is one the project
61
68
  did not ask for and did not write, the reconciliation already handles a missing
62
69
  side, and an empty override invites a target to fill it before it has an
@@ -113,4 +113,4 @@ One class stays open and is named rather than closed. A bullet can cite where so
113
113
 
114
114
  ## Where it runs
115
115
 
116
- `review-pr` Step 3 calls it and files an `unmet` path as a `should-fix` finding under the `**PR body**` block the stale ticked box already takes, since what both corrupt is the merge record rather than a file in the diff. A body is edited between pushes, so a finding names the head the comparison ran at. `unnamed` is read there and raised off no count, since the step's own history is a question sent over bullets that already answered it. `unresolved` and `incidental` are reported nowhere.
116
+ `review-pr` Step 3 calls it through its `references/key-changes.md` and files an `unmet` path as a `should-fix` finding under the `**PR body**` block the stale ticked box already takes, since what both corrupt is the merge record rather than a file in the diff. A body is edited between pushes, so a finding names the head the comparison ran at. `unnamed` is read there and raised off no count, since the step's own history is a question sent over bullets that already answered it. `unresolved` and `incidental` are reported nowhere.
@@ -33,16 +33,16 @@ its filename carries an image extension (`png`, `jpg`, `jpeg`, `gif`, `webp`,
33
33
  folder holds whatever else a project keeps beside its captures. This
34
34
  repository's own tree carries eight `.md` files, two `.sh` scripts, a `.tsv`,
35
35
  a `.json`, and an `.html` file against a single `.png`, and every one of them
36
- would render as a broken `![]()` embed without the extension filter.
36
+ would render as a broken image embed without the extension filter.
37
37
 
38
38
  ## What the record carries
39
39
 
40
40
  The record groups every evidence path by state, the remainder of its
41
41
  directory under the `evidence/` segment, and within a state by filename stem,
42
42
  so a `before/hero.png` and an `after/hero.png` naming the same case group as
43
- one entry rather than two unrelated files. Each entry carries whether it
44
- existed at the base commit, which decides whether the comment shows a base
45
- image or marks the case new.
43
+ one entry, not two files. Each entry carries whether it existed at the base
44
+ commit, so the comment shows a base image or marks it new. All-new states drop
45
+ the Base column and each image is an `<img width="1000">`.
46
46
 
47
47
  `reason` on the record is what a caller branches on, not the exit code:
48
48
 
@@ -70,9 +70,10 @@ shows a reviewer, each field present only when that comment holds it:
70
70
 
71
71
  | Field | What it carries |
72
72
  | ----------- | ----------------------------------------------------------------- |
73
- | `preview` | The hosted `**Preview:**` address the comment opens with. |
74
- | `local` | The `**Local preview:**` address under it. |
73
+ | `preview` | The hosted `**Preview:**` address on the comment's address line. |
74
+ | `local` | The `**Local preview:**` address beside it. |
75
75
  | `checklist` | The checklist between its delimiters, with any ticked boxes kept. |
76
+ | `boxes` | Each box's `number`, `text`, tick, `stamp`, and `isTaste`. |
76
77
 
77
78
  These come from the comment already posted, never from the flags the call
78
79
  passed, so a skill driving a review reads which address and which checklist a
@@ -102,7 +103,7 @@ The trade is that the verb no longer sees evidence that is uncommitted or
102
103
  unpushed, which the comment's head-pinned image links could not show anyway.
103
104
  A path the pull request renamed or copied counts as added, since the new path
104
105
  has no counterpart at the merge base, so a rename loses its before image and
105
- renders `*(new)*`. A path the pull request removed is dropped, since it has no
106
+ reads as new. A path the pull request removed is dropped, since it has no
106
107
  head image to show. A failed files read refuses as `unreadable-changes` rather
107
108
  than rendering a short set, and a missing merge base refuses as `no-base`.
108
109
 
@@ -142,11 +143,34 @@ details wrapper lands once, not twice.
142
143
 
143
144
  ## The preview line
144
145
 
145
- `--preview <url>` opens the body with the branch's preview address, and a run
146
+ Every body opens with `## Evidence`. Under it sits one address line carrying
147
+ whichever previews apply, joined by a middle dot:
148
+
149
+ ```plaintext
150
+ ## Evidence
151
+
152
+ **Preview:** <url> · **Local preview:** <url>
153
+
154
+ **Base:** `<base>` · **Head:** `<head>`
155
+ ```
156
+
157
+ The Base and Head line follows only when the comment carries screenshots.
158
+
159
+ `--preview <url>` puts the branch's hosted address on that line, and a run
146
160
  without it carries forward the address the marked comment already holds. With
147
- no evidence in the diff and an address in hand, the body is the address and the
148
- marker alone, reported as `ok`. `canon docs pr-preview` covers where the
149
- address comes from.
161
+ no evidence in the diff and an address in hand, the body is the heading, the
162
+ address line, and the marker alone, reported as `ok`. `canon docs pr-preview`
163
+ covers where the address comes from.
164
+
165
+ A carried hosted address is dropped when a deploy workflow resolves and its
166
+ push path filter matches no path the pull request changed, since the site it
167
+ links shows none of the change. An explicit `--preview` is always kept.
168
+
169
+ The carry-forward reads every line above the commit line, the checklist, and
170
+ the comparison for each prefix, wherever it sits. A comment posted before the
171
+ heading opened the body, with each address on a line of its own at the top,
172
+ reads the same way, so an open pull request keeps its links on its first
173
+ re-render.
150
174
 
151
175
  ## The checklist line
152
176
 
@@ -158,9 +182,9 @@ screenshots, and what a reviewer has to look at. The file is what
158
182
 
159
183
  A run without the option carries forward whatever checklist the marked comment
160
184
  already holds, the same way the preview address is carried. That is what keeps
161
- `git-followup`'s re-render after a push from wiping boxes a reviewer has
162
- already ticked, since `git-pr` deletes the handoff once the first post reports
163
- success and no later call has a file to pass.
185
+ `git-followup`'s re-render after a push from dropping the checklist, since
186
+ `git-pr` deletes the handoff once the first post reports success and no later
187
+ call has a file to pass. `canon docs pr-tick` covers what a tick carries.
164
188
 
165
189
  The option decides `no-evidence` on its own. A branch with a checklist and no
166
190
  changed evidence image renders `ok` with a marked body holding the checklist,
@@ -173,11 +197,10 @@ gone.
173
197
 
174
198
  ## The local preview line
175
199
 
176
- `--local <url>` adds `**Local preview:** <url>` to the opening block: under the
177
- hosted `**Preview:**` line when there is one, and as the first line when there
178
- is not. The hosted line always keeps the first position, since that is the one
179
- line the carry-forward reads for it. A run without the option carries forward
180
- the local address the marked comment already holds, the same as the other two.
200
+ `--local <url>` adds `**Local preview:** <url>` to the address line, after the
201
+ hosted segment when there is one and alone when there is not. A run without the
202
+ option carries forward the local address the marked comment already holds, the
203
+ same as the other two.
181
204
 
182
205
  The option does not turn `no-evidence` into `ok` on its own. A branch with a
183
206
  dev server running and no rendered change would otherwise get a comment holding
@@ -206,10 +229,11 @@ canon pr evidence 1341 --check --json
206
229
  - `evidence` is owed when the changed set carries an evidence image and no
207
230
  comment carries the marker.
208
231
  - `preview` is owed when the pull request has a marked comment or an evidence
209
- change, a deploy workflow resolves in the checkout the verb runs from, and
210
- the marked comment's first line is no `**Preview:**` line. A
211
- `**Local preview:**` line does not satisfy it. A project deploying nothing is
212
- never owed a preview.
232
+ change, a deploy workflow resolves in the checkout the verb runs from, its
233
+ push path filter matches a path the pull request changed, and the marked
234
+ comment carries no `**Preview:**` address. A `**Local preview:**` address
235
+ does not satisfy it. A project deploying nothing is never owed a preview,
236
+ and neither is a branch its deploy does not build from.
213
237
 
214
238
  The record carries `owed`, `commentId` when a marked comment exists, and the
215
239
  same `preview`, `local`, and `checklist` fields the other reasons carry, so a
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: The pull request local preview
3
- description: How canon pr local finds the server a worktree is running, why a server in another worktree is never reported, the refusal reasons it names, and how a close workflow removes the link with --remove
3
+ description: How canon pr local finds the server a worktree is running, why a server in another worktree is never reported, the refusal reasons it names, and how a close workflow drops the address line with --remove
4
4
  ---
5
5
 
6
6
  # The pull request local preview
@@ -10,7 +10,7 @@ worktree is running. `git-pr` calls it ahead of the evidence comparison and
10
10
  passes the address through `canon pr evidence --local`, so a reviewer on the
11
11
  same machine gets the running branch to click into beside the screenshots and
12
12
  the checklist. A workflow on the pull request's `closed` event calls it again
13
- with `--remove`, which replaces the line with a note.
13
+ with `--remove`, which drops the address line and leaves nothing in its place.
14
14
 
15
15
  ```bash
16
16
  canon pr local --json
@@ -62,9 +62,9 @@ to it on this pull request would show a reviewer the wrong code.
62
62
  | `ok` | A server answered. `url` carries the address and `port` the port. |
63
63
  | `no-server` | Nothing inside this worktree served an HTML page. |
64
64
  | `no-listener-reader` | Neither `lsof` nor `/proc` is available, so no socket could be read. |
65
- | `removed` | `--remove` replaced the local line on the evidence comment. |
65
+ | `removed` | `--remove` dropped the address line from the evidence comment. |
66
66
  | `no-comment` | `--remove` found no comment carrying the evidence marker. |
67
- | `no-line` | `--remove` found the comment but no local line on it. |
67
+ | `no-line` | `--remove` found the comment but no address line on it. |
68
68
  | `gh-missing` | `--remove` needs `gh` and it is not on the path. |
69
69
  | `gh-failed` | `--remove` could not read the comments or could not edit the comment. |
70
70
 
@@ -84,12 +84,19 @@ on:
84
84
  types: [closed]
85
85
  ```
86
86
 
87
- It reads the comment carrying the `pr-evidence` marker, replaces the
88
- `**Local preview:**` line with the `--note` text, and edits the comment itself
89
- through `gh api`. The verb writes here rather than handing a body back, because
90
- its caller is a workflow with no session to post one. Every other byte of the
91
- comment stays as it was, which keeps the marker, the checklist delimiters, and
92
- any ticked box intact. A second run finds no line and reports `no-line`.
87
+ It reads the comment carrying the `pr-evidence` marker, drops the address line
88
+ under `## Evidence`, the hosted `**Preview:**` segment and the
89
+ `**Local preview:**` segment together, and edits the comment itself through
90
+ `gh api`. The hosted link goes too because the deploy workflow deletes the
91
+ branch's preview deployments on the same close. No note replaces the line, and
92
+ the blank line it sat on goes with it so no gap doubles under the heading. A
93
+ comment in the older layout, with each address on its own line at the top,
94
+ loses both lines the same way.
95
+
96
+ The verb writes here rather than handing a body back, because its caller is a
97
+ workflow with no session to post one. Every other byte of the comment stays as
98
+ it was, which keeps the marker, the checklist delimiters, and any ticked box
99
+ intact. A second run finds no line and reports `no-line`.
93
100
 
94
101
  A fork pull request gets a read-only token, so the workflow skips the edit with
95
102
  a notice rather than failing the close.
@@ -100,6 +107,5 @@ The link can still go dead while the pull request is open, such as when the
100
107
  session holding the server retires. Only the close is covered, and a re-render
101
108
  after a push carries the line forward whether the server still answers or not.
102
109
 
103
- The hosted `**Preview:**` line from `canon pr preview` stays on the comment
104
- after close, even though its deployments are deleted then. `canon docs
105
- pr-preview` covers that path.
110
+ `canon docs pr-preview` covers how the hosted address is minted and when it is
111
+ refused.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: The pull request preview deploy
3
- description: How canon pr preview finds a fenced Cloudflare Pages deploy, dispatches it on a pull request's branch, reads the alias the run prints, the refusal reasons it names, and why an unfenced deploy is refused before anything runs
3
+ description: How canon pr preview finds a fenced Cloudflare Pages deploy, refuses a branch its push filter does not serve, dispatches it on a pull request's branch, reads the alias the run prints, the refusal reasons it names, and why an unfenced deploy is refused before anything runs
4
4
  ---
5
5
 
6
6
  # The pull request preview deploy
@@ -8,8 +8,8 @@ description: How canon pr preview finds a fenced Cloudflare Pages deploy, dispat
8
8
  `canon pr preview` publishes a pull request's branch to a Cloudflare Pages
9
9
  preview and reports the address. `git-pr` calls it after the evidence
10
10
  comparison, then re-renders that comment through `canon pr evidence --preview`
11
- so the link opens it. A reviewer gets a page to click into before merging
12
- rather than screenshots and a checklist alone.
11
+ so the link sits on the address line under its heading. A reviewer gets a page
12
+ to click into before merging rather than screenshots and a checklist alone.
13
13
 
14
14
  ```bash
15
15
  canon pr preview --json
@@ -31,6 +31,24 @@ the first file, in path order, that meets four tests:
31
31
  The workflow is read before the pull request, so a project with no fenced
32
32
  deploy is refused without a network call.
33
33
 
34
+ ## Which branches it serves
35
+
36
+ A dispatch runs the workflow whatever its `on.push.paths` filter says, so a
37
+ branch that touched nothing the site builds from would get a preview showing
38
+ none of its change. The verb reads the pull request's changed paths and matches
39
+ them against the picked workflow's own `on.push.paths` or `on.push.paths-ignore`
40
+ list, applying `!` patterns in order the way GitHub does. When nothing matches,
41
+ it refuses as `unserved` and dispatches nothing. `--check` answers `unserved`
42
+ the same way.
43
+
44
+ The push filter is the one statement of what the site builds from, since it
45
+ already decides when production deploys. A workflow with neither list, with no
46
+ push trigger, or that does not parse as YAML serves every change, so a project
47
+ whose every change ships keeps minting for every branch.
48
+
49
+ A removed path counts, the same as GitHub's own push filter counts it, so a
50
+ branch whose only site change deletes a file still gets a preview.
51
+
34
52
  ## Why an unfenced deploy is refused
35
53
 
36
54
  Cloudflare Pages publishes a deploy that names no branch to production. A
@@ -62,19 +80,21 @@ keeps the match independent of the local clock.
62
80
 
63
81
  `reason` on the record is what a caller branches on, not the exit code:
64
82
 
65
- | Reason | What it means |
66
- | --------------- | -------------------------------------------------------------------------- |
67
- | `ok` | The preview was published. `url` carries the address and `runId` the run. |
68
- | `no-deploy` | No dispatchable workflow runs `pages deploy`. |
69
- | `unfenced` | The deploy passes no `--branch`, so nothing was dispatched. |
70
- | `no-alias` | The workflow prints no alias line, or the finished run's log carried none. |
71
- | `run-failed` | The deploy run finished without succeeding. `runId` names it. |
72
- | `timeout` | The run did not finish inside `--timeout` minutes, 15 by default. |
73
- | `bad-timeout` | `--timeout` was not a positive number. |
74
- | `gh-missing` | `gh` is not on the path. |
75
- | `gh-failed` | `gh` could not read the pull request, or list or dispatch the workflow. |
76
- | `no-branch` | The pull request carries no head branch name. |
77
- | `check-timeout` | `--check` was combined with `--timeout`. |
83
+ | Reason | What it means |
84
+ | -------------------- | -------------------------------------------------------------------------- |
85
+ | `ok` | The preview was published. `url` carries the address and `runId` the run. |
86
+ | `no-deploy` | No dispatchable workflow runs `pages deploy`. |
87
+ | `unserved` | The push path filter matches no changed path, so nothing was dispatched. |
88
+ | `unreadable-changes` | The pull request's changed paths could not be read. |
89
+ | `unfenced` | The deploy passes no `--branch`, so nothing was dispatched. |
90
+ | `no-alias` | The workflow prints no alias line, or the finished run's log carried none. |
91
+ | `run-failed` | The deploy run finished without succeeding. `runId` names it. |
92
+ | `timeout` | The run did not finish inside `--timeout` minutes, 15 by default. |
93
+ | `bad-timeout` | `--timeout` was not a positive number. |
94
+ | `gh-missing` | `gh` is not on the path. |
95
+ | `gh-failed` | `gh` could not read the pull request, or list or dispatch the workflow. |
96
+ | `no-branch` | The pull request carries no head branch name. |
97
+ | `check-timeout` | `--check` was combined with `--timeout`. |
78
98
 
79
99
  The exit is 0 on `ok` and 1 on every refusal. A `timeout` leaves the run
80
100
  going, so the preview may still land after the verb has given up on it.
@@ -107,10 +127,11 @@ reason but `fresh`.
107
127
 
108
128
  ## How the address reaches the pull request
109
129
 
110
- `canon pr evidence --preview <url>` puts `**Preview:** <url>` on the first line
111
- of the evidence body. With no evidence image in the diff, the body is that line
112
- and the trailing marker alone, so a pull request whose screenshots did not
113
- change still gets one comment a later call can find and edit.
130
+ `canon pr evidence --preview <url>` puts `**Preview:** <url>` on the address
131
+ line under the evidence body's `## Evidence` heading. With no evidence image in
132
+ the diff, the body is the heading, that line, and the trailing marker alone, so
133
+ a pull request whose screenshots did not change still gets one comment a later
134
+ call can find and edit.
114
135
 
115
136
  A later `canon pr evidence` run without `--preview` reads the address off the
116
137
  marked comment and carries it into the new body. `git-followup` re-renders the
@@ -123,7 +144,9 @@ way, which is what keeps that push from wiping boxes a reviewer already ticked.
123
144
  A `pull_request` trigger on `closed` in the same workflow runs a cleanup job
124
145
  that deletes every preview deployment Cloudflare holds for the branch. The
125
146
  deploy job carries `if: github.event_name != 'pull_request'`, so a closed pull
126
- request never deploys.
147
+ request never deploys. The same close runs `canon pr local --remove`, which
148
+ drops the address line from the evidence comment, hosted link included, so the
149
+ comment never links a deployment that no longer exists.
127
150
 
128
151
  ## What this does not cover
129
152
 
@@ -0,0 +1,72 @@
1
+ ---
2
+ title: The pull request checklist tick
3
+ description: How canon pr tick marks the checklist boxes a UI pass drove, the stamp that names the commit each passed at, why a render at a newer head clears a tick, the refusal reasons it names, and why a taste box is never ticked
4
+ ---
5
+
6
+ # The pull request checklist tick
7
+
8
+ `canon pr tick` ticks the boxes of the visual checklist on the pull request's
9
+ evidence comment. `review-ui` calls it once its verdict has posted, for every
10
+ box that passed at the head it drove, so the person merging reads a checklist
11
+ a driver worked through instead of one that stays empty.
12
+
13
+ ```bash
14
+ canon pr tick 1341 --boxes 1,2,4 --head <sha> --json
15
+ ```
16
+
17
+ Box numbers come from the `boxes` field of `canon pr evidence --json`, which
18
+ numbers the checklist's box lines in order. A caller never counts lines itself.
19
+
20
+ ## What a tick looks like
21
+
22
+ A ticked box ends in the commit it passed at, visible on the comment:
23
+
24
+ ```markdown
25
+ - [x] click "Pricing" in the header → the pricing page opens · passed at `<short-sha>`
26
+ ```
27
+
28
+ The stamp is visible rather than an HTML comment because the person it is for
29
+ reads the merge record, and a hidden head would tell the machine what the
30
+ reader cannot see. The reader matches the stamp only as the trailing segment of
31
+ a line, so a box whose own text says `passed at` is left alone.
32
+
33
+ ## When a tick is cleared
34
+
35
+ A tick is a claim about one commit, so a render at another commit does not
36
+ carry it forward. `canon pr evidence` settles the checklist before it renders:
37
+
38
+ - A tick stamped for the render's head stays.
39
+ - A tick stamped for any other head is cleared and loses its stamp.
40
+ - A tick with no stamp, such as one an author or the operator added by hand,
41
+ stays only when the comment it came from described the render's head.
42
+
43
+ A render at the same head after `--preview` keeps every tick. A push that
44
+ reaches the branch without a render leaves its ticks in place until the next
45
+ one, and the stamp still shows which head earned them.
46
+
47
+ ## What it refuses
48
+
49
+ `reason` on the record is what a caller branches on, not the exit code:
50
+
51
+ | Reason | What it means |
52
+ | -------------- | ------------------------------------------------------------------------------------------------------------ |
53
+ | `ticked` | The boxes were ticked, with the remote tip in `head`. |
54
+ | `stale-head` | `--head` is not the branch tip on the remote. Nothing is written. |
55
+ | `taste-box` | A named box ends in `(taste)`. Nothing is written. |
56
+ | `no-box` | A named number is past the end of the checklist. Nothing is written. |
57
+ | `no-checklist` | The evidence comment carries no checklist. |
58
+ | `no-comment` | No comment on the pull request carries the evidence marker. |
59
+ | `bad-boxes` | `--boxes` is not a list of positive whole numbers, or `--head` is not a hex sha of seven characters or more. |
60
+
61
+ A taste box is a judgment of look or feel that a driver reports as needing a
62
+ person's eyes. Refusing it in the verb makes that rule a check rather than a
63
+ sentence a driver can talk itself out of, and a refusal writes none of the
64
+ boxes named beside it.
65
+
66
+ The head is compared against the tip read from the remote rather than the pull
67
+ request object, which lags a push by up to a minute. A push landing between
68
+ that read and the write leaves a tick at an older head until the next render
69
+ clears it, the same window `canon pr head` documents.
70
+
71
+ The verb edits the marked comment in place with a `PATCH`, the way
72
+ `canon pr local --remove` does, and ticking a box again restamps it.
@@ -9,7 +9,7 @@ description: Scenario routing, the expectation scoring surface, and the coverage
9
9
 
10
10
  ## Scenarios
11
11
 
12
- Scenarios live under `sandbox/`, one folder per category. `sandbox/fixtures/` is the exception, holding file content that scenarios stage rather than scenarios of its own, so the picker and the coverage report both filter it out. That tree sits outside `files` in `package.json`, so an installed `canon` carries the command, reports it as toolkit-only on one line, and exits 1 rather than failing on the missing directory. Route non-interactively with `SANDBOX_SCENARIO`:
12
+ Scenarios live under `sandbox/`, one folder per category and one file per command, either a TypeScript module or a bash script, and provisioning refuses a command present in both forms. `sandbox/fixtures/` is the exception, holding file content that scenarios stage rather than scenarios of its own, so the picker and the coverage report both filter it out. That tree sits outside `files` in `package.json`, so an installed `canon` carries the command, reports it as toolkit-only on one line, and exits 1 rather than failing on the missing directory. Route non-interactively with `SANDBOX_SCENARIO`:
13
13
 
14
14
  ```bash
15
15
  SANDBOX_SCENARIO=sync canon sandbox infra:tooling
@@ -39,15 +39,39 @@ The verdict `state` is `pass`, `fail`, or `unchecked`. An arm with no `expect.to
39
39
 
40
40
  Omitting `--writes`, `--escapes`, or `--envelope` does not silently drop the assertion kinds that need them. Write scope, escape scope, the turn ceiling, and the reply assertion report as unchecked and appear in the count, so the standalone command cannot claim more coverage than it had. Supplying `--escapes` without `--escapes-watched` reports the same way: a zero-escape result with no root confirmed watched is unmeasured rather than a pass. A verdict never reports `pass` with zero assertions.
41
41
 
42
- An arm declaring `escape_scope` asserts a bound on `run.sh`'s own escape watch rather than on the sandbox tree. `write_scope` skips when the run wrote nothing, since a required output missing is itself a finding, but `escape_scope` passes on zero escapes outright, since a clean run producing none is the expected outcome for a destination nothing requires a skill to touch, provided `--escapes-watched` confirms a root held something to watch. Declaring `escape_scope = []` asserts that a correct run reaches none of the watched destinations at all. `canon/context/sandbox/isolation.md` names what the watch reaches and what it cannot.
42
+ An arm declaring `escape_scope` asserts a bound on the headless runner's own escape watch rather than on the sandbox tree. `write_scope` skips when the run wrote nothing, since a required output missing is itself a finding, but `escape_scope` passes on zero escapes outright, since a clean run producing none is the expected outcome for a destination nothing requires a skill to touch, provided `--escapes-watched` confirms a root held something to watch. Declaring `escape_scope = []` asserts that a correct run reaches none of the watched destinations at all. `canon/context/sandbox/isolation.md` names what the watch reaches and what it cannot.
43
43
 
44
44
  An envelope that parses but carries no `result` field skips the reply assertion the same way an absent file does. An envelope carrying an empty `result` fails it, since a run that returned no text is a finding rather than a gap in the input.
45
45
 
46
46
  Exit 0 means `pass` or `unchecked`. Exit 1 means `fail`, or a caller error: a malformed target, or a sandbox that was never provisioned. A missing sandbox reports as an error rather than a failed verdict, because failing every path assertion would read as a skill that did nothing. `--strict` moves `unchecked` to exit 1 for a caller that has finished arming its scenarios.
47
47
 
48
+ ## Headless runs
49
+
50
+ `canon sandbox run` provisions a scenario, drives one `claude -p` session over it, and scores the result with `canon sandbox check`. `sandbox/run.sh` is a wrapper that passes its arguments to the verb, so either spelling runs the same code.
51
+
52
+ ```bash
53
+ canon sandbox run <category>:<command> "<prompt>" [arm]
54
+ canon sandbox run claude:plan-feature "/canon:plan-feature add a widget" small
55
+ ```
56
+
57
+ Every run bills a real session through the `claude` binary on `PATH`. The session runs with `--plugin-dir` pointed at this checkout's `claude/`, so it loads the branch's skills rather than an installed copy, and provisioning injects no skill into the tree for the same reason. Four variables override the flags it hands to `claude -p`:
58
+
59
+ | Variable | Default |
60
+ | ---------------------------------- | -------------------------------- |
61
+ | `CANON_SKILL_TEST_MODEL` | `sonnet` |
62
+ | `CANON_SKILL_TEST_TOOLS` | `Bash,Read,Glob,Grep,Edit,Write` |
63
+ | `CANON_SKILL_TEST_MAX_TURNS` | `30` |
64
+ | `CANON_SKILL_TEST_PERMISSION_MODE` | `bypassPermissions` |
65
+
66
+ The max-turns value is the only budget a run has. An arm's `max_turns` is asserted after the run and cannot raise it.
67
+
68
+ Stdout carries one JSON record: the session's envelope with `verdict`, `escapes`, and `sessions` merged in, described under the skills census below. Stderr carries the framed timeline, the provisioning output, the session's own stderr, the verdict report, and a warning for each escape, new session record, or reaped process. An envelope that does not parse still prints its verdict, under `is_error: true`.
69
+
70
+ Exit codes follow whichever step stopped the run. Provisioning's code ends a run before the session starts, and a session that exits non-zero ends it with that code after writing a dead-run record, `{is_error, exit_code, raw_output}`, to the same runs folder the merged record uses. Otherwise the verdict's code stands, except that an envelope that does not parse forces 1. An interrupt reaps the session's process group before the runner exits.
71
+
48
72
  ## Provisioning equivalence
49
73
 
50
- `canon sandbox equivalence` proves a change moved nothing in what scenarios provision. It provisions every arm of the named targets on a base ref and on the working checkout, builds a manifest of each tree, and names what differs. It runs offline and never calls `sandbox/run.sh` or a `claude` binary, so no arm it runs bills a model.
74
+ `canon sandbox equivalence` proves a change moved nothing in what scenarios provision. It provisions every arm of the named targets on a base ref and on the working checkout, builds a manifest of each tree, and names what differs. It runs offline and never calls `canon sandbox run` or a `claude` binary, so no arm it runs bills a model.
51
75
 
52
76
  ```bash
53
77
  canon sandbox equivalence claude --base origin/main --json
@@ -69,7 +93,7 @@ The baseline is a detached worktree with full history, removed on exit including
69
93
 
70
94
  The record lists per arm `identical`, `differs` with the manifest keys that differ, `red-on-base` when both sides fail the same way, or `skipped-anchor`. It also carries `canonVersion`, `baseCommit`, `masksApplied`, `errors`, and `counts`. A declared arm adds a `check` entry from `canon sandbox check` on the head tree, whose `asserted` count shows its `expect.toml` resolved from `sandbox/fixtures/`.
71
95
 
72
- Exit 0 means every compared arm is identical or red the same way on both sides. Exit 1 means any `differs`, a base and head exit that disagree, or an armed arm that `canon sandbox coverage` lists and enumeration lacks.
96
+ Exit 0 means every compared arm is identical or red the same way on both sides. Exit 1 means any `differs`, a base and head exit that disagree, an armed arm that `canon sandbox coverage` lists and enumeration lacks, a scenario present as both `.sh` and `.ts`, or a `.ts` scenario that fails to load.
73
97
 
74
98
  ## Scenario coverage
75
99
 
@@ -95,8 +119,8 @@ A skill pairs to a scenario by filename, `<category>-<command>` first and bare `
95
119
 
96
120
  `exempt` means no arm should be written and holds only with a reason, declared in `sandbox/exempt.toml` and limited to a harness limit the checker cannot reach past or a skill that writes no artifact. An armed arm outranks an exemption. An exemption naming no shipped skill, or naming one an arm now asserts, exits 1 without `--strict`. Each armed arm reports as `<category>:<command>/<arm>`, so two same-named arms under different scenarios stay distinct.
97
121
 
98
- `sandbox/run.sh` calls this after a headless run and merges the verdict into the envelope it prints. It also writes that merged record to `.canon/tmp/runs/sandbox/<target>-<arm>-<timestamp>.json` with a `writes` array appended, and logs the path on stderr. Both fields are what a later re-score needs, since `--envelope` and `--writes` read files the run deletes on exit.
122
+ `canon sandbox run` calls this after a headless run and merges the verdict into the envelope it prints. It also writes that merged record to `.canon/tmp/runs/sandbox/<target>-<arm>-<timestamp>.json` with a `writes` array appended, and logs the path on stderr. Both fields are what a later re-score needs, since `--envelope` and `--writes` read files the run deletes on exit.
99
123
 
100
124
  Two more fields ride alongside the verdict rather than inside it. `escapes` lists what the run wrote under a watched toolkit root, which the verdict cannot assert over because those files sit outside the sandbox tree. `sessions` reports the nested-dispatch bound, carrying `watched` for whether the client's session registry was there to read, `new` for the records that appeared while the run was in flight, `concurrent` for the records present both before and after, and `reap` for what the run found in the session's process group afterwards. Neither field fails a run on its own.
101
125
 
102
- `run.sh` passes `concurrent` through `--concurrent-sessions` to `sandbox check`, and `checkEscapeScope` appends a witness count to an `unbounded escape:` message rather than lets it soften the verdict.
126
+ `canon sandbox run` passes `concurrent` through `--concurrent-sessions` to `sandbox check`, and `checkEscapeScope` appends a witness count to an `unbounded escape:` message rather than lets it soften the verdict.
@@ -17,7 +17,7 @@ Use these to discover what's available instead of hardcoding names.
17
17
  | `canon standards list --json` | Standards docs and the paths each governs |
18
18
  | `canon gov list --json` | Governance stacks, rule sets, and unreferenced rules |
19
19
  | `canon claude seeds list --json` | Seed doc sources with content |
20
- | `canon claude skills list --json` | Plugin skills, descriptions, requirement flag |
20
+ | `canon claude skills list --json` | Plugin skills, descriptions, requirement flag, family vocabulary |
21
21
  | `canon docs list --json` | Consumer docs plus per-domain context |
22
22
  | `canon audits list --json` | Audits the set runs, the corpus each reads, and whether it gates |
23
23
 
@@ -68,6 +68,13 @@ or unparseable returns an empty description rather than failing the listing, so
68
68
  one malformed file cannot hide the rest of the catalog. `--names` emits skill
69
69
  names one per line.
70
70
 
71
+ Each skill in `--json` carries `family`, the `metadata.family` its frontmatter
72
+ declares, or `null` where it declares none. A top-level `families` array holds
73
+ the vocabulary as `{ key, group }` entries in skill map order, where `group` is
74
+ the map heading the family's rows sit under. Read a new skill's family from it
75
+ rather than from the map. `--by-family` prints the human listing as one step per
76
+ family, with a trailing step for any skill whose family is missing or unknown.
77
+
71
78
  Each entry also carries `requirement`, whether the folder holds a sibling
72
79
  `REQUIREMENT.md`. Every skill is meant to carry one, so a `false` is a gap to
73
80
  close rather than a recorded exemption, and the flag answers which skills are
@@ -38,6 +38,7 @@ Each check traces to a line in the standard.
38
38
  - No ISO date in `SKILL.md` or any file under `references/`, reported as `datedProvenance` in the JSON record's `findings`
39
39
  - Each listed practice skill closing with `## Excuses and rebuttals`, `## Red flags`, and `## Before handing over`, and each skill on the wider ledger list carrying `references/adopted.md`, reported together as `practiceShape`
40
40
  - Each file under `references/` running over 100 lines opening with a `## Contents` H2, reported as `referenceContents`
41
+ - Each skill under `claude/skills/` declaring a `metadata.family` that names a key in `checkpoints.families`, reported as `family`
41
42
 
42
43
  A body whose frontmatter does not parse reports as declaring neither field rather than ending the run, so one malformed file cannot hide the corpus behind it. A key present with an empty value reads as absent, since a blank `name` would otherwise report as a name disagreeing with every folder. A folder carrying no requirement is reported once under presence rather than counted again for the sections it therefore lacks.
43
44
 
@@ -61,7 +62,7 @@ A check with no rule behind it prints an opinion as a defect, which is where the
61
62
 
62
63
  ## Exit codes
63
64
 
64
- Exit codes are `0` for a clean run, `1` for a refusal, and `2` for a skill folder carrying no `REQUIREMENT.md`. Only presence sets a failing code. Name, description, folder, requirement-section, dated-provenance, practice shape, and reference contents findings print and return `0`, because each is a judgment a reader settles and failing a push on one would make the check something to route around. In the toolkit, two `bun run check` stages read the same report in-process and fail a push on dated provenance and on practice shape, since its own corpus is held at zero on both.
65
+ Exit codes are `0` for a clean run, `1` for a refusal, and `2` for a skill folder carrying no `REQUIREMENT.md`. Only presence sets a failing code. Name, description, folder, requirement-section, dated-provenance, practice shape, reference contents, and family findings print and return `0`, because each is a judgment a reader settles and failing a push on one would make the check something to route around. In the toolkit, three `bun run check` stages read the same report in-process and fail a push on dated provenance, on practice shape, and on family, since its own corpus is held at zero on all three.
65
66
 
66
67
  No stage reads reference contents, so a long reference without a list stays a report.
67
68
 
@@ -59,7 +59,7 @@ The question block is read through the same parser `canon tasks validate` runs,
59
59
 
60
60
  Branch on `launchable` rather than on the exit code, for the reason the section above states: a shell profile wrapping `canon` in a function can take a later command's status and mask every non-zero exit, which reads a waiting plan as a launchable one.
61
61
 
62
- The orchestrator dispatch runbook calls this before it checks the branch or the file sets, so a row whose plan still needs a person is handed back rather than launched into a worker that halts on the same question.
62
+ The orchestrator dispatch runbook calls this before it checks the branch or runs its conflict check, so a row whose plan still needs a person is handed back rather than launched into a worker that halts on the same question.
63
63
 
64
64
  ```bash
65
65
  canon tasks plan-answers dispatch-answer-gate --json | jq -r '.launchable'