@erclx/canon 5.32.0 → 5.34.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 (250) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/api-design/REQUIREMENT.md +1 -2
  3. package/claude/skills/api-design/SKILL.md +2 -3
  4. package/claude/skills/api-design/references/adopted.md +1 -1
  5. package/claude/skills/auto-ship/SKILL.md +1 -1
  6. package/claude/skills/auto-ship/references/worktree-name.md +1 -1
  7. package/claude/skills/backlog-triage/REQUIREMENT.md +2 -2
  8. package/claude/skills/backlog-triage/SKILL.md +1 -1
  9. package/claude/skills/canon-cli/REQUIREMENT.md +16 -13
  10. package/claude/skills/canon-cli/SKILL.md +75 -32
  11. package/claude/skills/canon-feedback/REQUIREMENT.md +1 -0
  12. package/claude/skills/canon-feedback/SKILL.md +11 -29
  13. package/claude/skills/canon-feedback/references/template.md +53 -0
  14. package/claude/skills/{canvas → canvas-board}/REQUIREMENT.md +2 -2
  15. package/claude/skills/{canvas → canvas-board}/SKILL.md +6 -3
  16. package/claude/skills/{canvas → canvas-board}/references/canvas.md +26 -3
  17. package/{standards → claude/skills/canvas-board/references}/design.md +11 -2
  18. package/claude/skills/code-craft/REQUIREMENT.md +1 -1
  19. package/claude/skills/code-craft/SKILL.md +0 -1
  20. package/claude/skills/context-fold/REQUIREMENT.md +25 -33
  21. package/claude/skills/context-fold/SKILL.md +25 -70
  22. package/claude/skills/context-fold/references/classify.md +3 -3
  23. package/claude/skills/context-fold/references/completion-report.md +4 -4
  24. package/claude/skills/context-fold/references/context-refresh.md +5 -5
  25. package/{standards → claude/skills/context-fold/references}/context.md +16 -5
  26. package/claude/skills/context-fold/references/unusable-baseline.md +3 -5
  27. package/claude/skills/create-rule/SKILL.md +3 -3
  28. package/{standards → claude/skills/create-rule/references}/rule.md +15 -6
  29. package/claude/skills/create-skill/SKILL.md +2 -2
  30. package/{standards → claude/skills/create-skill/references}/skill.md +18 -15
  31. package/claude/skills/design-taste/REQUIREMENT.md +3 -3
  32. package/claude/skills/design-taste/SKILL.md +1 -1
  33. package/claude/skills/docs-sync/REQUIREMENT.md +1 -1
  34. package/claude/skills/docs-sync/SKILL.md +3 -3
  35. package/{standards → claude/skills/docs-sync/references}/docs.md +11 -0
  36. package/{standards → claude/skills/docs-sync/references}/readme.md +9 -2
  37. package/claude/skills/draft-and-pick/SKILL.md +5 -5
  38. package/claude/skills/draft-doc/SKILL.md +6 -6
  39. package/claude/skills/draft-figure/REQUIREMENT.md +2 -2
  40. package/claude/skills/draft-figure/SKILL.md +2 -2
  41. package/claude/skills/draft-figure/references/hand-drawn.md +1 -1
  42. package/claude/skills/draft-figure/references/mermaid-path.md +1 -1
  43. package/claude/skills/draft-identity/REQUIREMENT.md +2 -2
  44. package/claude/skills/draft-identity/SKILL.md +2 -2
  45. package/claude/skills/draft-ready/SKILL.md +1 -1
  46. package/claude/skills/git-branch/SKILL.md +1 -1
  47. package/{standards → claude/skills/git-branch/references}/branch.md +1 -1
  48. package/claude/skills/git-commit/SKILL.md +2 -2
  49. package/claude/skills/git-followup/REQUIREMENT.md +1 -1
  50. package/claude/skills/git-followup/SKILL.md +1 -1
  51. package/claude/skills/git-issue/SKILL.md +1 -1
  52. package/claude/skills/git-issue/references/issue.md +4 -1
  53. package/claude/skills/git-pr/SKILL.md +13 -13
  54. package/claude/skills/git-pr/references/evidence.md +2 -2
  55. package/claude/skills/git-pr/references/final-command.md +38 -14
  56. package/claude/skills/git-pr/references/labels.md +5 -1
  57. package/{standards → claude/skills/git-pr/references}/pr.md +19 -9
  58. package/{standards → claude/skills/git-pr/references}/publish.md +4 -2
  59. package/claude/skills/git-pr/references/task-number.md +1 -1
  60. package/claude/skills/git-ship/SKILL.md +15 -14
  61. package/claude/skills/git-split/SKILL.md +3 -3
  62. package/claude/skills/git-stage/SKILL.md +1 -1
  63. package/claude/skills/plan-feature/REQUIREMENT.md +1 -1
  64. package/claude/skills/review-address/SKILL.md +1 -1
  65. package/claude/skills/review-address/references/post-review-findings.md +1 -1
  66. package/claude/skills/review-craft/references/ui.md +1 -1
  67. package/claude/skills/review-pr/REQUIREMENT.md +1 -1
  68. package/claude/skills/review-pr/SKILL.md +3 -3
  69. package/claude/skills/review-pr/references/body-variants.md +2 -2
  70. package/claude/skills/review-pr/references/key-changes.md +3 -3
  71. package/claude/skills/review-pr/references/post.md +4 -2
  72. package/claude/skills/review-ui/REQUIREMENT.md +1 -1
  73. package/claude/skills/review-ui/SKILL.md +8 -6
  74. package/claude/skills/review-ui/references/driving.md +8 -8
  75. package/claude/skills/review-ui/references/post.md +53 -17
  76. package/claude/skills/role-orchestrator/REQUIREMENT.md +3 -0
  77. package/claude/skills/role-orchestrator/SKILL.md +2 -2
  78. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +2 -2
  79. package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +8 -5
  80. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +32 -1
  81. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +2 -2
  82. package/claude/skills/role-orchestrator/references/orchestrator-resume.md +3 -0
  83. package/claude/skills/role-planner/REQUIREMENT.md +2 -1
  84. package/claude/skills/role-planner/SKILL.md +13 -2
  85. package/claude/skills/role-reviewer/SKILL.md +1 -1
  86. package/claude/skills/role-worker/REQUIREMENT.md +1 -0
  87. package/claude/skills/role-worker/SKILL.md +9 -1
  88. package/claude/skills/session-map/SKILL.md +1 -1
  89. package/claude/skills/session-resume/REQUIREMENT.md +2 -2
  90. package/claude/skills/session-worktree/SKILL.md +5 -5
  91. package/claude/skills/target-setup/SKILL.md +10 -4
  92. package/claude/skills/target-setup/references/detect.md +20 -3
  93. package/claude/skills/task-board/REQUIREMENT.md +7 -12
  94. package/claude/skills/task-board/SKILL.md +5 -17
  95. package/claude/skills/task-board/references/tasks.md +2 -2
  96. package/claude/skills/task-complete/REQUIREMENT.md +47 -0
  97. package/claude/skills/task-complete/SKILL.md +86 -0
  98. package/claude/skills/{task-board → task-complete}/references/archive.md +4 -4
  99. package/claude/skills/{context-fold → task-complete}/references/receipt-sweep.md +5 -5
  100. package/claude/skills/task-complete/references/unusable-baseline.md +19 -0
  101. package/claude/skills/teach-workspace/REQUIREMENT.md +6 -2
  102. package/claude/skills/teach-workspace/SKILL.md +8 -4
  103. package/claude/skills/teach-workspace/references/lesson-craft.md +1 -1
  104. package/claude/skills/{context-fold/references/promoted-pages.md → teach-workspace/references/promotion-fold.md} +3 -3
  105. package/claude/skills/teach-workspace/references/promotion.md +3 -3
  106. package/claude/skills/test-craft/REQUIREMENT.md +2 -3
  107. package/claude/skills/test-craft/SKILL.md +15 -3
  108. package/claude/skills/ui-checklist/SKILL.md +1 -1
  109. package/claude/skills/ux-audit/SKILL.md +1 -1
  110. package/claude/skills/ux-measure/SKILL.md +1 -1
  111. package/docs/agents/canvas.md +6 -2
  112. package/docs/agents/commands.md +4 -3
  113. package/docs/agents/context-audit-checks.md +4 -4
  114. package/docs/agents/gov-detect.md +47 -0
  115. package/docs/agents/index.md +7 -4
  116. package/docs/agents/indexes.md +1 -1
  117. package/docs/agents/install-and-sync.md +10 -11
  118. package/docs/agents/key-changes.md +5 -5
  119. package/docs/agents/label-coverage.md +11 -0
  120. package/docs/agents/migrate.md +1 -1
  121. package/docs/agents/pr-evidence-layout.md +70 -0
  122. package/docs/agents/pr-evidence.md +8 -9
  123. package/docs/agents/pr-frames.md +7 -2
  124. package/docs/agents/pr-lift.md +132 -0
  125. package/docs/agents/records-push.md +2 -2
  126. package/docs/agents/records-stale.md +12 -47
  127. package/docs/agents/records-validate.md +3 -7
  128. package/docs/agents/records.md +5 -27
  129. package/docs/agents/rule-citations.md +1 -1
  130. package/docs/agents/sandbox.md +4 -4
  131. package/docs/agents/scripting.md +2 -1
  132. package/docs/agents/skills-audit.md +1 -1
  133. package/docs/agents/tasks-plans.md +2 -2
  134. package/docs/agents/tasks.md +2 -2
  135. package/docs/target/migrations.md +10 -2
  136. package/docs/target/projects.md +4 -4
  137. package/docs/target/sync.md +1 -1
  138. package/docs/workflow/operating-model.md +2 -2
  139. package/docs/workflow/parallel-features.md +7 -7
  140. package/docs/workflow/skill-map-on-request.md +9 -9
  141. package/docs/workflow/skill-map.md +12 -11
  142. package/docs/workflow/visual-design-workflow.md +2 -2
  143. package/governance/detect.toml +201 -0
  144. package/governance/renames.toml +0 -11
  145. package/governance/rules/canon/context.md +13 -0
  146. package/governance/rules/claude/skill.md +17 -0
  147. package/governance/rules/code/000-code.md +1 -13
  148. package/governance/rules/code/010-testing.md +2 -30
  149. package/governance/rules/ui/400-ui.md +1 -1
  150. package/governance/rules/ui/480-canvas.md +13 -0
  151. package/governance/stacks/base.toml +2 -4
  152. package/package.json +4 -4
  153. package/scripts/core/install-check.sh +3 -3
  154. package/scripts/core/regen-hero.ts +0 -6
  155. package/scripts/lib/ui.sh +0 -168
  156. package/src/audits/catalog.ts +0 -1
  157. package/src/canvas/arrange.ts +66 -0
  158. package/src/canvas/capture.ts +20 -6
  159. package/src/canvas/content.ts +39 -0
  160. package/src/canvas/fit.ts +238 -0
  161. package/src/canvas/tokens.ts +28 -10
  162. package/src/capture/frame-values.ts +2 -32
  163. package/src/claude/cases/misc.ts +1 -1
  164. package/src/claude/cases/setup.ts +4 -0
  165. package/src/claude/cases/workflow.ts +6 -1
  166. package/src/claude/seeds.ts +1 -1
  167. package/src/claude/skills-audit.ts +2 -2
  168. package/src/cli/help.ts +3 -1
  169. package/src/cli/ui.ts +2 -3
  170. package/src/commands/canvas.ts +179 -1
  171. package/src/commands/claude.ts +1 -1
  172. package/src/commands/gov.ts +146 -16
  173. package/src/commands/labels.ts +326 -7
  174. package/src/commands/migrate.ts +6 -1
  175. package/src/commands/pr.ts +377 -2
  176. package/src/commands/records.ts +13 -399
  177. package/src/commands/sandbox.ts +3 -6
  178. package/src/commands/tasks.ts +3 -3
  179. package/src/context/audit.ts +5 -5
  180. package/src/context/folders.ts +1 -1
  181. package/src/design/budget.ts +1 -1
  182. package/src/design/components.ts +1 -1
  183. package/src/design/document.ts +1 -1
  184. package/src/design/regen.ts +1 -1
  185. package/src/design/render.ts +1 -1
  186. package/src/feedback/body.ts +26 -5
  187. package/src/gate/measures.ts +59 -45
  188. package/src/gate/sequencer.ts +56 -1
  189. package/src/gate/stages.ts +4 -11
  190. package/src/gov/detect.ts +301 -0
  191. package/src/gov/install.ts +2 -3
  192. package/src/gov/list.ts +23 -2
  193. package/src/gov/stacks.ts +10 -1
  194. package/src/hooks/post-merge.ts +1 -1
  195. package/src/labels/apply.ts +39 -0
  196. package/src/labels/format.ts +3 -3
  197. package/src/labels/phase.ts +3 -3
  198. package/src/labels/sections.ts +43 -0
  199. package/src/migrate/skill-names.ts +20 -1
  200. package/src/pr/evidence.ts +218 -45
  201. package/src/pr/frames.ts +23 -3
  202. package/src/pr/lift.ts +418 -0
  203. package/src/pr/paths.ts +4 -4
  204. package/src/pr/rest.ts +3 -0
  205. package/src/pr/review-scope.ts +76 -0
  206. package/src/records/backup.ts +16 -6
  207. package/src/records/stale-canonical.ts +31 -1
  208. package/src/records/validate.ts +2 -188
  209. package/src/sandbox/census.ts +1 -1
  210. package/src/sandbox/coverage.ts +2 -6
  211. package/src/sandbox/equivalence.ts +19 -51
  212. package/src/sandbox/expect.ts +4 -4
  213. package/src/sandbox/provision.ts +13 -36
  214. package/src/sandbox/scenario.ts +26 -48
  215. package/src/sandbox/tree.ts +2 -6
  216. package/src/standards/read.ts +14 -1
  217. package/src/sync/git.ts +1 -1
  218. package/src/tasks/archive.ts +5 -5
  219. package/src/tasks/branch.ts +3 -3
  220. package/src/tasks/label.ts +1 -1
  221. package/src/tasks/record.ts +2 -2
  222. package/src/tasks/validate.ts +1 -1
  223. package/standards/index.md +0 -13
  224. package/standards/standard.md +1 -7
  225. package/tooling/base/reference.md +1 -1
  226. package/tooling/base/seeds/.github/pull_request_template.md +2 -2
  227. package/tooling/claude/reference.md +1 -1
  228. package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +9 -1
  229. package/tooling/claude/seeds/.claude/hooks/review-post-scan.sh +171 -0
  230. package/tooling/claude/seeds/.claude/settings.json +9 -0
  231. package/governance/rules/standards/context.md +0 -14
  232. package/governance/rules/standards/design.md +0 -12
  233. package/governance/rules/standards/readme.md +0 -12
  234. package/governance/rules/standards/rule.md +0 -13
  235. package/governance/rules/standards/skill.md +0 -21
  236. package/governance/rules/standards/standard.md +0 -13
  237. package/scripts/config.sh +0 -11
  238. package/scripts/lib/gov.sh +0 -14
  239. package/scripts/lib/sandbox-fixtures.sh +0 -191
  240. package/scripts/lib/sandbox-git.sh +0 -125
  241. package/scripts/lib/sandbox-path.sh +0 -50
  242. package/scripts/sandbox-hook.sh +0 -99
  243. package/src/gov/standard-rules.ts +0 -179
  244. package/src/records/migrate.ts +0 -78
  245. package/src/records/stale.ts +0 -243
  246. package/src/sandbox/hooks.ts +0 -128
  247. /package/{standards → claude/skills/draft-figure/references}/figures.md +0 -0
  248. /package/{standards → claude/skills/git-branch/references}/slug.md +0 -0
  249. /package/{standards → claude/skills/git-commit/references}/commit.md +0 -0
  250. /package/{standards → claude/skills/git-commit/references}/versioning.md +0 -0
@@ -5,6 +5,16 @@ description: Pull request title and body conventions
5
5
 
6
6
  # Pull request reference
7
7
 
8
+ ## Contents
9
+
10
+ - [Scope](#scope)
11
+ - [Title](#title)
12
+ - [Content](#content)
13
+ - [Sections](#sections)
14
+ - [Testing discipline](#testing-discipline)
15
+ - [Formatting](#formatting)
16
+ - [Examples](#examples)
17
+
8
18
  ## Scope
9
19
 
10
20
  Governs a pull request title and body: their format and the sections the body carries.
@@ -37,16 +47,16 @@ Does not govern:
37
47
  ## Sections
38
48
 
39
49
  - `## Summary`: 1-2 sentences following `<Action Verb> <Direct Object> to <Result>`, expand for clarity if needed
40
- - `## Key Changes`: name actual files, functions, or modules (e.g., `AuthService.verify()` not "auth handler"). Always use bullet points, never prose.
41
- - `## Technical Context` (optional): 1-2 lines of architectural reasoning explaining why, not what
42
- - Omit Technical Context for docs, config, or trivial changes
50
+ - `## Key changes`: name actual files, functions, or modules (e.g., `AuthService.verify()` not "auth handler"). Always use bullet points, never prose.
51
+ - `## Technical context` (optional): 1-2 lines of architectural reasoning explaining why, not what
52
+ - Omit Technical context for docs, config, or trivial changes
43
53
  - Use bullet points for multiple reasons, one sentence for a single reason
44
54
  - `## Testing` (optional): specify exact commands or test cases run
45
55
  - Omit Testing for docs, config, or trivial sync changes
46
56
  - Use checkboxes, never prose. See Testing discipline for which box gets ticked.
47
57
  - `## For the reviewer` (optional): what the reviewing session should confirm, one bullet per request
48
58
  - Visuals: include only when they clarify architecture, UI, or complex logic flows
49
- - The list is closed. A body carries the sections above and nothing after the last of them, which covers a trailer a harness appends once the body is composed. `publish.md` states the rule, and `canon labels scan` fails on the one such trailer measured so far.
59
+ - The list is closed. A body carries the sections above and nothing after the last of them, which covers a trailer a harness appends once the body is composed. `canon labels scan` holds the list: it fails a level-two heading outside the five above, and a body missing `## Summary` or `## Key changes`. A review body and a release-please body are exempt.
50
60
 
51
61
  ## Testing discipline
52
62
 
@@ -74,12 +84,12 @@ Does not govern:
74
84
 
75
85
  <Action Verb> <Direct Object> to <Result>.
76
86
 
77
- ## Key Changes
87
+ ## Key changes
78
88
 
79
89
  - <Verb> <specific component/file/function> (<reason if non-obvious>)
80
90
  - <Verb> <specific component/file/function>
81
91
 
82
- ## Technical Context
92
+ ## Technical context
83
93
 
84
94
  - <Architectural reasoning explaining why, not what>
85
95
 
@@ -101,12 +111,12 @@ Does not govern:
101
111
 
102
112
  Update auth middleware to enforce jwt expiration checks. # imperative + direct object + result
103
113
 
104
- ## Key Changes
114
+ ## Key changes
105
115
 
106
116
  - Add `verifyExpiration()` to `src/auth/validators.ts`. # specific function + file path
107
117
  - Refactor `AuthService.authenticate()` to handle 401 codes. # named component + clear change
108
118
 
109
- ## Technical Context
119
+ ## Technical context
110
120
 
111
121
  - Migration to stateless session management for horizontal scalability. # why, not what
112
122
 
@@ -128,7 +138,7 @@ Update auth middleware to enforce jwt expiration checks. # imperative + direct o
128
138
 
129
139
  This PR updates the authentication system to be more robust. # "This PR" opener + buzzword
130
140
 
131
- ## Key Changes
141
+ ## Key changes
132
142
 
133
143
  - Updated auth middleware files # vague, no specific component, no period
134
144
  - The old system used to check tokens differently # describes historical behavior
@@ -26,13 +26,15 @@ Scope every check below by destination. Text published to a remote takes all of
26
26
 
27
27
  ## Banned characters
28
28
 
29
- `markdown.md` holds the character bans. Read it at scan time rather than working the sets from memory, then scan the drafted text and rewrite each occurrence.
29
+ `${CLAUDE_SKILL_DIR}/../markdown-craft/references/markdown.md` holds the character bans. Read it at scan time rather than working the sets from memory, then scan the drafted text and rewrite each occurrence.
30
+
31
+ `canon labels scan` fails on these characters in a title or body, outside a code span or a link, so the scan is the check and the read above is the reason. It holds a pull request and a posted review alike.
30
32
 
31
33
  Restructure the sentence rather than substituting the character. A semicolon swapped for a period leaves both clauses in the order the semicolon chose, which is the shape the ban exists to remove.
32
34
 
33
35
  ## Phase labels
34
36
 
35
- `versioning.md` holds the label rule and the table of surfaces. Read it at scan time rather than working the format from memory.
37
+ `${CLAUDE_SKILL_DIR}/../git-commit/references/versioning.md` holds the label rule and the table of surfaces. Read it at scan time rather than working the format from memory.
36
38
 
37
39
  This check is one of the two the destination rule above scopes. The reader inside the repository has the task board and the reader on a remote does not.
38
40
 
@@ -9,7 +9,7 @@ The last step of `git-pr` before its report, reached on every run whose final co
9
9
 
10
10
  Write the `number` the final command printed onto the task the branch is closing. Do not resolve it again. A final command that refused on a head mismatch printed no number, so this step writes nothing on that run. `${CLAUDE_SKILL_DIR}/REQUIREMENT.md` states why: a lookup that resolves by branch alone can return a closed pull request sharing that head, so the number is resolved once and reused rather than re-derived.
11
11
 
12
- The task is the one whose `Plan:` line names the plan this branch implemented. Name that plan by its file, which is `.canon/plans/feature-<slug>.md` at the main worktree root with `<slug>` derived per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. `plan-feature` writes the plan under the branch slug, so the two correspond on any branch that came through the plan-to-execute path. When the session already knows which plan it implemented, because a caller read it earlier in the chain, use that filename instead of re-deriving.
12
+ The task is the one whose `Plan:` line names the plan this branch implemented. Name that plan by its file, which is `.canon/plans/feature-<slug>.md` at the main worktree root with `<slug>` derived per `${CLAUDE_SKILL_DIR}/../git-branch/references/slug.md`. `plan-feature` writes the plan under the branch slug, so the two correspond on any branch that came through the plan-to-execute path. When the session already knows which plan it implemented, because a caller read it earlier in the chain, use that filename instead of re-deriving.
13
13
 
14
14
  ```bash
15
15
  canon tasks pull-request <number> --plan feature-<slug> --json
@@ -9,7 +9,7 @@ metadata:
9
9
 
10
10
  Run the full post-feature workflow by invoking each skill in sequence using the Skill tool. After each skill returns, invoke the next step immediately in the same response.
11
11
 
12
- Do not output any text between steps and do not wait for user input. Tool permission dialogs are the only interrupts allowed. The final output is `✅ Shipped`, emitted on the wake after the step 8 watch exits on `passing` rather than when the turn ends on the watch line, unless a wrapping caller states it closes on its own block, which `auto-ship` does.
12
+ Do not output any text between steps and do not wait for user input. Tool permission dialogs are the only interrupts allowed. The final output is `✅ Shipped`, emitted on the wake after the step 9 watch exits on `passing` rather than when the turn ends on the watch line, unless a wrapping caller states it closes on its own block, which `auto-ship` does.
13
13
 
14
14
  ## Verify
15
15
 
@@ -25,16 +25,17 @@ Run `git diff --cached --name-only 2>/dev/null` to check for staged files. If ou
25
25
 
26
26
  ## Sequence
27
27
 
28
- 1. Invoke `canon:context-fold` to sync internal planning docs against session decisions
29
- 2. Invoke `canon:docs-sync` to sync public docs against changes since main
30
- 3. Run `git add -A` to stage any files the sync skills wrote
31
- 4. Run `canon tasks plan-reach <plan> --json` and report both lists it carries. Name the plan this branch built under, by path or by slug. Read `claimed` first and say who holds each path, since that is the half a reader acts on, then say how many of the changed paths `undeclared` names. Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero. This step reports and never stops the sequence.
32
- 5. Invoke `canon:git-stage` to group staged changes and commit by concern, only when `git diff --cached --name-only` prints a path. When it prints nothing, the work already reached history in slices, so continue to step 6.
33
- 6. Invoke `canon:git-branch` to rename branch to match conventional format
34
- 7. Invoke `canon:git-pr` to push branch and open pull request
35
- 8. After the PR opens, watch CI in the background, so the session stays reachable while CI runs. Read `### Watching CI` below for the commands and the wake.
28
+ 1. Invoke `canon:context-fold` to sync the context entries against session decisions and the diff
29
+ 2. Invoke `canon:task-complete` to mark the outcomes the diff shipped
30
+ 3. Invoke `canon:docs-sync` to sync public docs against changes since main
31
+ 4. Run `git add -A` to stage any files the sync skills wrote
32
+ 5. Run `canon tasks plan-reach <plan> --json` and report both lists it carries. Name the plan this branch built under, by path or by slug. Read `claimed` first and say who holds each path, since that is the half a reader acts on, then say how many of the changed paths `undeclared` names. Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero. This step reports and never stops the sequence.
33
+ 6. Invoke `canon:git-stage` to group staged changes and commit by concern, only when `git diff --cached --name-only` prints a path. When it prints nothing, the work already reached history in slices, so continue to step 7.
34
+ 7. Invoke `canon:git-branch` to rename branch to match conventional format
35
+ 8. Invoke `canon:git-pr` to push branch and open pull request
36
+ 9. After the PR opens, watch CI in the background, so the session stays reachable while CI runs. Read `### Watching CI` below for the commands and the wake.
36
37
 
37
- A caller wrapping this sequence may act between step 7 and step 8, which is the one gap the order leaves open, since the pull request exists there and nothing has read its checks yet. `auto-ship` marks the pull request draft in it. Nothing else may go there, and a caller that needs a step anywhere else in the sequence is asking for a change to this body rather than for a place to stand.
38
+ A caller wrapping this sequence may act between step 8 and step 9, which is the one gap the order leaves open, since the pull request exists there and nothing has read its checks yet. `auto-ship` marks the pull request draft in it. Nothing else may go there, and a caller that needs a step anywhere else in the sequence is asking for a change to this body rather than for a place to stand.
38
39
 
39
40
  ### Watching CI
40
41
 
@@ -61,18 +62,18 @@ Do not auto-fix any of these. The watch line and a stop report from any branch a
61
62
 
62
63
  ### Settling the evidence thread
63
64
 
64
- On a `passing` wake, run `canon pr evidence <number> --check --json` against the number step 7 returned. It reads the pull request's comments and edits none, so the comment a reviewer already ticked stays as it is. Branch on `reason` and `owed` rather than the exit.
65
+ On a `passing` wake, run `canon pr evidence <number> --check --json` against the number step 8 returned. It reads the pull request's comments and edits none, so the comment a reviewer already ticked stays as it is. Branch on `reason` and `owed` rather than the exit.
65
66
 
66
67
  - `settled`: nothing is owed, which is also what a branch that changed no rendered surface reads. Continue to After completion.
67
68
  - `owed`: run each step the record's `owed` list names, from `canon:git-pr`'s `references/evidence.md`, evidence first and then preview. Re-read once with the same command. Continue to After completion only on `settled`. Any other reading stops the sequence and reports the `reason` with the `owed` list, and the sequence never loops back for a second pass. A preview step that refuses, or a deploy that failed, leaves `preview` owed on the re-read, so that is the stop and not a retry.
68
69
  - Any other `reason`, such as `gh-failed`: the thread went unread. Stop and report the `reason`, since an unread thread is not a settled one.
69
70
  - No record at all, or an unknown flag error: the installed binary predates `--check`. Report the thread unread rather than settled, and continue to After completion without emitting a claim that it was checked.
70
71
 
71
- The read is a closing step and the draft mark stays the only thing between steps 7 and 8. A caller that closes on its own block, as `auto-ship` does, waits on the same CI wake, so the read lands here and not in that body.
72
+ The read is a closing step and the draft mark stays the only thing between steps 8 and 9. A caller that closes on its own block, as `auto-ship` does, waits on the same CI wake, so the read lands here and not in that body.
72
73
 
73
- ### Why the reach reads at step 4
74
+ ### Why the reach reads at step 5
74
75
 
75
- The branch is whole there and nowhere earlier. Steps 1 and 2 write past whatever the plan declared, so a reading taken ahead of them misses the chain's own additions, and no pull request exists until step 7 to carry the answer anywhere.
76
+ The branch is whole there and nowhere earlier. Steps 1 and 3 write past whatever the plan declared, so a reading taken ahead of them misses the chain's own additions, and no pull request exists until step 8 to carry the answer anywhere.
76
77
 
77
78
  It is the one step here that reports rather than acts, which is why it never stops the sequence. Every crossing measured on the wave it was filed against merged clean, and the undeclared list runs long enough on an ordinary branch that a gate would fire on nearly every ship.
78
79
 
@@ -9,8 +9,8 @@ metadata:
9
9
 
10
10
  Before proposing a split, read in parallel:
11
11
 
12
- - `${CLAUDE_SKILL_DIR}/../../standards/branch.md`: format, types, length limit, and constraints
13
- - `${CLAUDE_SKILL_DIR}/../../standards/pr.md`: PR title format, body sections, and content rules
12
+ - `${CLAUDE_SKILL_DIR}/../git-branch/references/branch.md`: format, types, length limit, and constraints
13
+ - `${CLAUDE_SKILL_DIR}/../git-pr/references/pr.md`: PR title format, body sections, and content rules
14
14
 
15
15
  Follow both exactly.
16
16
 
@@ -71,7 +71,7 @@ If Mode is Independent, append this line to the preview:
71
71
 
72
72
  After outputting the preview, execute the final commands immediately. Claude Code's tool permission dialog is the confirmation gate. Do not wait for user input.
73
73
 
74
- Before running them, run the scan in `${CLAUDE_SKILL_DIR}/../../standards/publish.md` against every title and body. Each body stages under `.canon/tmp/` and leaves through `gh`, so the hook sees neither and this scan is the only gate. Each resulting branch reaches the `pull_request` check the git-pr surface carries once its own pull request opens, but not before, so this scan is what catches a leak ahead of that.
74
+ Before running them, run the scan in `${CLAUDE_SKILL_DIR}/../git-pr/references/publish.md` against every title and body. Each body stages under `.canon/tmp/` and leaves through `gh`, so the hook sees neither and this scan is the only gate. Each resulting branch reaches the `pull_request` check the git-pr surface carries once its own pull request opens, but not before, so this scan is what catches a leak ahead of that.
75
75
 
76
76
  ### Final commands
77
77
 
@@ -9,7 +9,7 @@ metadata:
9
9
 
10
10
  Read these files in parallel:
11
11
 
12
- - `${CLAUDE_SKILL_DIR}/../../standards/commit.md`: format, types, scopes, and constraints
12
+ - `${CLAUDE_SKILL_DIR}/../git-commit/references/commit.md`: format, types, scopes, and constraints
13
13
  - `${CLAUDE_SKILL_DIR}/../markdown-craft/references/markdown.md`: punctuation and formatting for all generated text
14
14
  - The `write-human` skill: voice, rhythm, and sentence construction for all generated text
15
15
 
@@ -40,4 +40,4 @@ Without this skill, implementation starts before anyone knows what it touches. A
40
40
 
41
41
  - Executing the plan, which is the ship pipeline
42
42
  - Task-board state, which `task-board` owns. A plan links to its task and does not create one.
43
- - Reconciling the planning docs after the work lands, which `context-fold` owns
43
+ - Reconciling the context entries after the work lands, which `context-fold` owns, and marking the board, which `task-complete` owns
@@ -156,7 +156,7 @@ the regen rebuilt, and close the same way. The heading stays outside the
156
156
  `## Review` family so the close-out's equality test on the first line never
157
157
  matches it.
158
158
 
159
- Before posting, follow `${CLAUDE_SKILL_DIR}/../../standards/publish.md`
159
+ Before posting, follow `${CLAUDE_SKILL_DIR}/../git-pr/references/publish.md`
160
160
  and run its scan against the reply:
161
161
 
162
162
  ```bash
@@ -7,7 +7,7 @@ description: How review-address posts something worth reporting after Step 7 clo
7
7
 
8
8
  Outside the numbered flow of `review-address`. The session reads this file when it has something worth reaching the reviewing session after Step 7 already closed the review.
9
9
 
10
- Not everything worth reaching the reviewing session surfaces inside the numbered flow in `SKILL.md`. A worker that settled a risk, filed a follow-up, or found something else worth reporting after Step 7 already closed the review posts it directly rather than waiting on a review pass that has nothing left to trigger it. Write the body the way Step 6 writes a reply: load `write-human` for voice and word choice, follow `${CLAUDE_SKILL_DIR}/../markdown-craft/references/markdown.md` for punctuation, and run the `${CLAUDE_SKILL_DIR}/../../standards/publish.md` scan before posting with `canon labels scan --body-file .canon/tmp/pr/reply/reply-<number>.md`.
10
+ Not everything worth reaching the reviewing session surfaces inside the numbered flow in `SKILL.md`. A worker that settled a risk, filed a follow-up, or found something else worth reporting after Step 7 already closed the review posts it directly rather than waiting on a review pass that has nothing left to trigger it. Write the body the way Step 6 writes a reply: load `write-human` for voice and word choice, follow `${CLAUDE_SKILL_DIR}/../markdown-craft/references/markdown.md` for punctuation, and run the `${CLAUDE_SKILL_DIR}/../git-pr/references/publish.md` scan before posting with `canon labels scan --body-file .canon/tmp/pr/reply/reply-<number>.md`.
11
11
 
12
12
  Open with `## Post-review findings` rather than `## Review response`, since nothing on the thread is being answered. `review-pr` states the full heading set this belongs to and routes it the same as a response: `role-orchestrator`'s poll picks it up and sends the reviewing session back for a pass. Close the body with `🤖 Addressed by Claude Code` on its own line, matching the reply's footer.
13
13
 
@@ -45,7 +45,7 @@ mkdir -p .canon/tmp/pr/review/evidence/<number>
45
45
  git show <sha>:<path> > .canon/tmp/pr/review/evidence/<number>/<side>-<name>.png
46
46
  ```
47
47
 
48
- Take the sha from the row, so the base and head sides are exactly what the comment shows. Then open each file with the file-reading tool. A `*(new)*` base cell has no before image, and a state whose cases are all added has no Base column at all. Read the URL out of the `src=` of each `<img>`, or out of `![](` in a comment posted before the embed changed.
48
+ Take the sha from the row, so the base and head sides are exactly what the comment shows. Then open each file with the file-reading tool. A `*(new)*` base cell has no before image, and a view or flat block whose cases are all added has no base column at all. A view row holds one case per theme cell, so a width row names two images where a flat row names one. Read the URL out of the `src=` of each `<img>`, or out of `![](` in a comment posted before the embed changed.
49
49
 
50
50
  ## How many to open
51
51
 
@@ -29,7 +29,7 @@ Scoping a later pass off GitHub's review stamp skipped real work. On 2026-09-07
29
29
  - Key the body file on the pull request number and the head commit, and on the response it answers once the head repeats, so no two passes overwrite each other
30
30
  - Link a file-bound finding to its line at the reviewed head, since a bare file block leaves a reader hunting the diff for the spot
31
31
  - Scan the comment for banned characters and internal phase labels before posting, since a finding phrased against a phase label reaches a reader with no task board
32
- - Answer a `## For the reviewer` bullet in the body, bounded to the section itself rather than the Summary or the Technical Context around it, so the independent vantage stays clear of the author's framing
32
+ - Answer a `## For the reviewer` bullet in the body, bounded to the section itself rather than the Summary or the Technical context around it, so the independent vantage stays clear of the author's framing
33
33
  - Confirm each `**Review focus:**` item the plan names on a first pass, and turn one the pass cannot confirm into a finding, since the planning session named the breaking inputs before the diff existed and no other reader holds that list
34
34
 
35
35
  ## Must not
@@ -79,15 +79,15 @@ Read each test file the branch added or changed against the final filter in `can
79
79
 
80
80
  Then read the description's `## Testing` section, which is the one part of a pull request body this session is positioned to falsify. The branch author cannot see what a sibling branch drove the same day, and the arm list is what settles the claim rather than the diff.
81
81
 
82
- Test every unchecked box against the testing discipline in `${CLAUDE_SKILL_DIR}/../../standards/pr.md`, which reserves an unchecked box for a capability the agent lacks. Raise the box when it names no human at all, when the human it names is a live agent session, or when it names a person for a step the repository ships a harness for, such as `sandbox/run.sh`. Cost alone is not an answer, since authorizing a spend is the operator's and performing the run is not. A refusal the author actually met is an answer, and it names which one.
82
+ Test every unchecked box against the testing discipline in `${CLAUDE_SKILL_DIR}/../git-pr/references/pr.md`, which reserves an unchecked box for a capability the agent lacks. Raise the box when it names no human at all, when the human it names is a live agent session, or when it names a person for a step the repository ships a harness for, such as `canon sandbox run`. Cost alone is not an answer, since authorizing a spend is the operator's and performing the run is not. A refusal the author actually met is an answer, and it names which one.
83
83
 
84
84
  Test every ticked box too, bounded to one naming a file or a command. Confirm what it names still exists at `<headRefOid>`, `git show <headRefOid>:<path>` for a file or `git grep <command> <headRefOid>` for a command, rather than re-running what it claims. A box naming neither stays untested, since nothing here can confirm a claim carrying no artifact. A box ticked before a later commit removed or replaced what it names is a `should-fix` finding on the body itself, filed under a `**PR body**` block ahead of the file blocks, since what it corrupts is the merge record rather than a file in the diff.
85
85
 
86
- Then test what `## Key Changes` claims against the diff it describes. Read `${CLAUDE_SKILL_DIR}/references/key-changes.md` for the verb that compares them, which field of its record becomes a finding, and which ones raise nothing.
86
+ Then test what `## Key changes` claims against the diff it describes. Read `${CLAUDE_SKILL_DIR}/references/key-changes.md` for the verb that compares them, which field of its record becomes a finding, and which ones raise nothing.
87
87
 
88
88
  Ask rather than grade. Whether a human is genuinely required is a reading the branch author may hold a reason this session cannot see, so the question carries no severity and enters no count. What it does carry is the heading and the dispatch, on the rule the opening states, because a question the author never receives corrects nothing and the author is the only party who can answer it. Answering it closes it, and the answer may be that the requirement holds.
89
89
 
90
- Read `## For the reviewer` the same way, bounded to the bullets under that heading rather than the Summary or the Technical Context around it. Those two carry the author's argument for the change, and reading them while judging it is most of what an independent pass exists to avoid, so the read stops at the section itself.
90
+ Read `## For the reviewer` the same way, bounded to the bullets under that heading rather than the Summary or the Technical context around it. Those two carry the author's argument for the change, and reading them while judging it is most of what an independent pass exists to avoid, so the read stops at the section itself.
91
91
 
92
92
  Answer each bullet in the body, on the same terms as a Testing question: no severity, no count. A request nobody can answer stays owed under the heading and dispatch the opening states, keeping the thread open exactly as an unanswered Testing box does. One the pass does answer carries no further weight, since the answer is discharged in the same comment that carries it.
93
93
 
@@ -12,7 +12,7 @@ A Testing box the Step 3 check raised goes in a `**Testing**` block placed after
12
12
  ```markdown
13
13
  **Testing**
14
14
 
15
- - `- [ ] <the box as written>` names no capability the agent lacks. `sandbox/run.sh <arm>` drives it. Was there a reason to leave it?
15
+ - `- [ ] <the box as written>` names no capability the agent lacks. `canon sandbox run <arm>` drives it. Was there a reason to leave it?
16
16
  ```
17
17
 
18
18
  Keep it to the boxes the check raised. Restating a box whose stated requirement holds teaches the branch author to skip the block.
@@ -24,7 +24,7 @@ An unanswered bullet is owed the same way an unanswered Testing box is, so a pas
24
24
  ```markdown
25
25
  **For the reviewer**
26
26
 
27
- - Confirm the 401 and 403 split reads correctly for the public API. Confirmed — `AuthService.authenticate()` returns 401 for an expired token and 403 for a missing scope, and both paths are covered under `## Testing`.
27
+ - Confirm the 401 and 403 split reads correctly for the public API. Confirmed: `AuthService.authenticate()` returns 401 for an expired token and 403 for a missing scope, and both paths are covered under `## Testing`.
28
28
  ```
29
29
 
30
30
  A pass carrying nothing at all takes `## Review closed` and a short body, with the footer line included either way. On a first pass, post `✅ No findings. Reviewed against project docs and the board.` On a later pass, post `✅ Prior findings addressed. Re-reviewed <short-sha>, N commits since the prior pass.`
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  title: Key changes check
3
- description: How review-pr Step 3 tests what the pull request body's Key Changes section claims against the diff, reading each field the canon pr key-changes record carries and which one becomes a finding
3
+ description: How review-pr Step 3 tests what the pull request body's Key changes section claims against the diff, reading each field the canon pr key-changes record carries and which one becomes a finding
4
4
  ---
5
5
 
6
- # Test the Key Changes claims
6
+ # Test the Key changes claims
7
7
 
8
8
  Part of Step 3 of `review-pr`. The session reads this file after the `## Testing` boxes and before the `## For the reviewer` bullets.
9
9
 
10
- Test what `## Key Changes` claims against the diff it describes, with `canon pr key-changes <number> --json`. This repository squash-merges, so that section becomes the commit message and the record on the trunk once the branch is gone, which is the same thing a stale ticked box corrupts. Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero.
10
+ Test what `## Key changes` claims against the diff it describes, with `canon pr key-changes <number> --json`. This repository squash-merges, so that section becomes the commit message and the record on the trunk once the branch is gone, which is the same thing a stale ticked box corrupts. Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero.
11
11
 
12
12
  Read `unmet` as a `should-fix` finding under the same `**PR body**` block the stale box takes, one entry per path, quoting the bullet the record carries on `preview` and naming the `head` the comparison ran at. Read the bullet before filing it, since one class survives the reader: a bullet citing where something is defined while claiming an edit elsewhere, which puts a real path in the claim region and points the change at a locative the path does not name. Report that as the class rather than as a stale claim. A body is edited between pushes, so the reading is true at that commit and can be false a minute later. Read `unnamed` for yourself and raise nothing off the count. The class still covers a real omission and equally a generated asset or a regenerated index that earns no bullet, and the entries the reader can name as owing none are already held apart under `incidental`. Open the section and confirm no bullet names the file before putting a single one to the author, because this instruction has sent the question to three pull requests in one day over bullets that had named the files all along, and one of those authors added bullets nobody needed. Report `unresolved` and `incidental` nowhere: the first was judged in neither direction and the second owes no bullet, so neither says anything about the body.
13
13
 
@@ -56,7 +56,7 @@ X critical, Y should-fix, Z minor. Reviewed against project docs and the board.
56
56
 
57
57
  A stale ticked box goes in a `**PR body**` block, in place of a `**`path/to/file.ext`**` block and ahead of every one of those, since it precedes the code the diff carries rather than sitting inside it.
58
58
 
59
- Run `canon labels scan --title "<title>" --body "<body>" --head <headRefName>` against the PR under review, since Step 1 already holds all three and this pass is the last human-shaped gate before merge. A hit lands in the same `**PR body**` block, `should-fix`, naming each finding the scan returns: a token quoted from the title or body for a phase label, a board identifier, a session link, or an unspelled word, and the broken rule's name, `structure`, a casing issue, or `length`, for a title-format hit. This reads the pull request being reviewed, distinct from the comment this pass is about to post, which the scan later in this step still covers.
59
+ Run `canon labels scan --title "<title>" --body "<body>" --head <headRefName>` against the PR under review, since Step 1 already holds all three and this pass is the last human-shaped gate before merge. A hit lands in the same `**PR body**` block, `should-fix`, naming each finding the scan returns: a token quoted from the title or body for a phase label, a board identifier, a session link, or an unspelled word, and the broken rule's name, `structure`, a casing issue, or `length`, for a title-format hit. A heading in `sections.outside`, a heading in `sections.missing`, or a character in `bannedCharacters` is a hit of the same weight, naming the heading or the character. This reads the pull request being reviewed, distinct from the comment this pass is about to post, which the scan later in this step still covers.
60
60
 
61
61
  A later pass carrying findings keeps that shape and changes only the summary line:
62
62
 
@@ -116,7 +116,9 @@ Write it as a comment rather than as prose so a reader of the thread never meets
116
116
 
117
117
  It is inert data, so the `publish.md` scan below and `canon labels scan` have nothing to fire on. Confirm that against the posted body rather than assuming it, since a phase label or a board identifier appearing inside a commit sha is not a shape either scan was written against.
118
118
 
119
- Before posting, run the scan in `${CLAUDE_SKILL_DIR}/../../standards/publish.md` against the body. The hook skips `.canon/tmp/`, so this scan is the only gate ahead of the post. A finding phrased against an internal phase label is what the label half of the scan catches here. This repository reads the same text again once posted, on `phase-label-gate.yml`'s `pull_request_review` trigger, which is what closes the gap this scan leaves open for a review a person writes and posts by hand with no scripted step in front of it. That workflow now reaches every target on the `base` stack, seeded at `tooling/base/configs/.github/workflows/phase-label-gate.yml` invoking the published CLI rather than this checkout's own source tree, so a target's coverage extends past this pre-post scan to the same post-trigger re-read this repository gets.
119
+ Before posting, run `canon labels scan --body-file .canon/tmp/pr/review/body-<number>-<short-sha>.md` against the body, which applies the rule in `${CLAUDE_SKILL_DIR}/../git-pr/references/publish.md`. Fix the body and scan again on a finding. A finding phrased against an internal phase label is what the label half of the scan catches here. The call carries no `--title`, so the scan reads the text as a review.
120
+
121
+ A `PreToolUse` hook on `Bash` runs the same scan against the `-F body=@<file>` of the post below and denies the call on a hit, so the check holds whether or not this step ran it. The hook reads the file before the command runs, so write the body in an earlier call than the post, and pass it as a file rather than inline. This repository reads the same text again once posted, on `phase-label-gate.yml`'s `pull_request_review` trigger, which is what closes the gap for a review a person writes and posts by hand with no hook in front of it. That workflow now reaches every target on the `base` stack, seeded at `tooling/base/configs/.github/workflows/phase-label-gate.yml` invoking the published CLI rather than this checkout's own source tree, so a target's coverage extends past the hook to the same post-trigger re-read this repository gets.
120
122
 
121
123
  Do not run the command below when `<prior-heading>` from Step 2 reads `## Review closed` and this pass carries nothing owed. That pass replaces the standing comment rather than adding one, so read `${CLAUDE_SKILL_DIR}/references/close-out.md` instead of posting. Posting first and reaching that section afterward leaves two close-outs both naming the new head, which is worse than the pair the guard exists against.
122
124
 
@@ -34,7 +34,7 @@ A verdict also goes stale without saying so. A push after the pass changes what
34
34
 
35
35
  ## Must not
36
36
 
37
- - Read the diff, the plan, the task, or the description's Summary and Technical Context. A pass that has heard the author's argument drives towards what it expects.
37
+ - Read the diff, the plan, the task, or the description's Summary and Technical context. A pass that has heard the author's argument drives towards what it expects.
38
38
  - Judge a box marked as taste
39
39
  - Check out, build, or serve the head, or download a browser mid-pass
40
40
  - Tick a taste box, a failed box, or a box at a head it did not drive, or tick any box other than through `canon pr tick`
@@ -46,7 +46,7 @@ gh api --paginate 'repos/{owner}/{repo}/issues/<number>/comments?per_page=100' -
46
46
 
47
47
  The first read prints the verdict's body and when it was posted as one JSON line, and the second prints when the newest reply was. `--paginate` runs the filter once per page, so `tail` outside it keeps the newest row in the thread rather than the newest on each page. The verdict goes out as JSON because its body spans many lines, and `tail` over the raw body would keep only its last one. An empty first read is a pull request carrying no verdict yet. Take the sha off the body's last-line `<!-- review-ui: head=<sha> -->` marker. A body carrying none covers no head this pass can trust, so drive.
48
48
 
49
- Read nothing else about the change. The diff, the plan, the task, and the description's Summary and Technical Context all carry the author's argument for it, and a pass that has heard it drives towards what it expects.
49
+ Read nothing else about the change. The diff, the plan, the task, and the description's Summary and Technical context all carry the author's argument for it, and a pass that has heard it drives towards what it expects.
50
50
 
51
51
  ## Step 2: read the evidence record
52
52
 
@@ -94,11 +94,13 @@ Read `${CLAUDE_SKILL_DIR}/references/driving.md` on reaching this step for how a
94
94
 
95
95
  Give every box exactly one verdict, in checklist order. A box the pass could not place on a route or a width is still driven, from the opened address at 1440 wide, and the verdict names the guess. Skipping a box silently reads the same as passing it.
96
96
 
97
- Never judge a box ending in `(taste)`. Drive to the state, capture the frame, and mark it needs eyes. Drive a ticked box the same as an unchecked one, since a tick is a claim this pass exists to check. A tick stamped at the current head still gets driven.
97
+ Never judge a box ending in `(taste)`. Drive to the state, capture both themes, and mark it needs eyes. Drive a ticked box the same as an unchecked one, since a tick is a claim this pass exists to check. A tick stamped at the current head still gets driven.
98
98
 
99
- A frame the verdict rests on is published only when the worker's evidence lacks it. Compare the box's route, width, and theme against the `states` Step 2 kept, and when a state and stem show the same thing, cite that stem and push nothing. The match is this pass's judgment over the box's wording, so the verb never makes it. Otherwise push the frame and keep the `link` the record returns:
99
+ A frame the verdict rests on is published only when the worker's evidence lacks it, matched per theme against the `states` Step 2 kept, as `references/driving.md` states. The match is this pass's judgment over the box's wording, never the verb's. Otherwise push the frame and keep the `link` the record returns:
100
100
 
101
101
  ```bash
102
+ # one shot of a taste box's pair, then a pass or fail frame with no theme
103
+ canon pr frames <number> --add frames/<box-number>-<theme>.png --box <box-number> --theme <dark|light> --head <head> --pass <pass> --json
102
104
  canon pr frames <number> --add frames/<box-number>.png --box <box-number> --head <head> --pass <pass> --json
103
105
  ```
104
106
 
@@ -118,7 +120,7 @@ Close the browser with `${CLAUDE_SKILL_DIR}/scripts/pw.sh -s=<project>-<number>
118
120
 
119
121
  ## Step 7: post
120
122
 
121
- Read `${CLAUDE_SKILL_DIR}/references/post.md` for the heading rule, the body shape, the marker every body ends on, and the post command. Each Evidence cell holds words and names the evidence stem it matched, and the `link` its Step 5 push returned goes as given into the Frames block below the table.
123
+ Read `${CLAUDE_SKILL_DIR}/references/post.md` for the heading rule, the body shape, the marker every body ends on, and the post command. Each box lands in the section its verdict names, and the `link` its Step 5 push returned goes as given into that box's own entry.
122
124
 
123
125
  ## Step 8: tick the passed boxes
124
126
 
@@ -139,10 +141,10 @@ gh run list --workflow phase-label-gate.yml --event pull_request_review --limit
139
141
  Read `<heading>` off the first line of the file Step 7 posted, never composed here, so the line reports what the review carries.
140
142
 
141
143
  ```plaintext
142
- P passed, F failed, E need eyes, N not driven. Posted to PR #<number> under <heading>.
144
+ F failed · E need your eyes · P passed · N not driven. Posted to PR #<number> under <heading>.
143
145
  ```
144
146
 
145
- Add `Review-event checks: <passing|failing|unread>.` and `Ticks: <ticked N|reason>.` after it, the second naming the count Step 8 ticked or the verb's refusal `reason`. Add `S sweep finding(s).` when the sweep raised any. Report no merge recommendation, since this pass saw the app and not the change.
147
+ Add `Review-event checks: <passing|failing|unread>.` and `Ticks: <ticked N|reason>.` after it, the second naming the count ticked or the refusal `reason`. Add `S sweep finding(s).` when the sweep raised any. Report no merge recommendation, since this pass saw the app and not the change.
146
148
 
147
149
  ## Rules
148
150
 
@@ -32,20 +32,20 @@ A box written before that format may carry no route, no width, and a verb such a
32
32
  - Text, presence, and attributes: `pw --raw eval "<expression>"`, quoting the value back.
33
33
  - Computed style: `getComputedStyle(<element>).<property>`.
34
34
  - Geometry and overflow: `getBoundingClientRect()`, and `scrollWidth` against `clientWidth` on `document.documentElement`.
35
- - A frame: `pw screenshot --filename=frames/<box-number>.png`, then open it with the file-reading tool before saying what it shows.
35
+ - A frame: `pw screenshot --filename=frames/<box-number>.png`, then open it with the file-reading tool before saying what it shows. A taste box takes the shot twice, once per theme, naming the theme in the file as `frames/<box-number>-<theme>.png`. Reach each theme through the page's own control when the box names one, and through `pw set-color-scheme <dark|light>` otherwise, then read the page's background before the shot to confirm it changed. A page that paints the same under both has no second theme to show, so the box is not driven, with the reason naming that. `pw clear-color-scheme` restores the default.
36
36
 
37
37
  ## The four verdicts
38
38
 
39
39
  Each box takes exactly one.
40
40
 
41
- | Verdict | When | Evidence it carries |
42
- | ------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
43
- | pass or fail, read | The result is a fact the page reports | The value read, quoted, such as `scrollWidth 436 against clientWidth 320` |
44
- | pass or fail, frame | The result has a stated criterion only an image settles | The matching evidence stem or the pushed frame's link going to the Frames block, and what the frame shows, in words |
45
- | needs eyes | The box ends in `(taste)` | The matching evidence stem or the pushed frame's link going to the Frames block, and what the frame showed, with nothing about whether it looks right |
46
- | not driven | The state could not be reached | The reason, such as an unreachable address, a `(local only)` box on a hosted preview, or a target the snapshot does not hold |
41
+ | Verdict | When | Evidence it carries |
42
+ | ------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
43
+ | pass or fail, read | The result is a fact the page reports | The value read, quoted, such as `scrollWidth 436 against clientWidth 320` |
44
+ | pass or fail, frame | The result has a stated criterion only an image settles | The matching evidence stem or the pushed frame's link, inline under the failed box's heading, and what the frame shows, in words |
45
+ | needs eyes | The box ends in `(taste)` | The dark and the light shot as a pair, each the matching evidence stem or a pushed frame's link, and what the frames showed, with nothing about whether it looks right |
46
+ | not driven | The state could not be reached | The reason, such as an unreachable address, a `(local only)` box on a hosted preview, or a target the snapshot does not hold |
47
47
 
48
- A frame verdict carries one image at most, and never one the worker's evidence already shows. When a state and stem in the evidence record match the box's route, width, and theme, the evidence names that stem and nothing is pushed. Otherwise the frame goes up through `canon pr frames` and the `link` the record returned, which names the frame's path on the frames branch, goes under the Frames block and never inside the table row. Either way the words stay, describing the value or region judged, such as the element, its position, and the colors or text read, so a reader whose image fails to load can still check it.
48
+ A pass or fail resting on a frame carries one image at most, and never one the worker's evidence already shows. A needs-eyes box carries the pair, dark and light, since a taste call is made by comparing them. Match each theme separately: when a state and stem in the evidence record match the box's route, width, and that theme, the evidence names that stem and nothing is pushed for it, so a view with the dark shot in evidence pushes the light one alone. Otherwise the frame goes up through `canon pr frames` with `--theme`, and the `link` the record returned, which names the frame's path on the frames branch, goes in the box's own section of the body and never inside a folded block. Either way the words stay, describing the value or region judged, such as the element, its position, and the colors or text read, so a reader whose image fails to load can still check it.
49
49
 
50
50
  A push that refuses leaves the words alone, with the reason named. Never cite the local frame path. A path under `.canon/` is a board identifier the label scan rejects, and it resolves only on this machine.
51
51
 
@@ -14,7 +14,7 @@ The heading reports whether the branch owes anything, the way the code review's
14
14
  - `## UI review` when a box failed, the sweep found a console error or an overflow, or a box was not driven for a cause on the page, such as a target the snapshot does not hold.
15
15
  - `## UI review closed` otherwise.
16
16
 
17
- A needs-eyes box leaves the review closed, and so does a box not driven for a cause off the page: an address that did not answer, or a `(local only)` box on a hosted preview. Each is owed to the person who merges rather than to the branch, and no commit on the branch can settle it, so opening the heading would hold the draft mark at every head. Name each such box in the summary line's not-driven count and in its row's evidence. Do not append the pull request number, which GitHub already renders.
17
+ A needs-eyes box leaves the review closed, and so does a box not driven for a cause off the page: an address that did not answer, or a `(local only)` box on a hosted preview. Each is owed to the person who merges rather than to the branch, and no commit on the branch can settle it, so opening the heading would hold the draft mark at every head. Name each such box in the count line's not-driven count and in its Not driven entry. Do not append the pull request number, which GitHub already renders.
18
18
 
19
19
  ## The body
20
20
 
@@ -23,19 +23,39 @@ Write it to `.canon/tmp/pr/review-ui/body-<number>-<short-sha>.md` at the main w
23
23
  ```markdown
24
24
  <## UI review, or ## UI review closed, by the rule above>
25
25
 
26
- P passed, F failed, E need eyes, N not driven. Drove <address> at <short-sha>, rendered by <renderer>.
26
+ F failed · E need your eyes · P passed · N not driven
27
+ Driven at `<short-sha>` on <address>, rendered by <renderer>.
27
28
 
28
- | Box | Evidence |
29
- | -------------------------------------------- | --------------------------------------------- |
30
- | **Fail.** 1. <the box as written, shortened> | <the value read against what the box expects> |
31
- | **Needs eyes.** 2. <box> | <what the frame showed, in words> |
32
- | **Pass.** 3. <box> | Evidence `<stem>` <what it showed, in words> |
29
+ ### Failed
33
30
 
34
- **Frames**
31
+ **<n>. <the box as written, shortened>**
32
+ <the value read against what the box expects>
35
33
 
36
- - Box 2, needs eyes:
34
+ ![box <n>](link)
37
35
 
38
- ![](link)
36
+ ### Needs your eyes
37
+
38
+ **<n>. <box> (taste)**
39
+
40
+ | Dark | Light |
41
+ | ---------------- | ----------------- |
42
+ | ![](<dark link>) | ![](<light link>) |
43
+
44
+ ### Not driven
45
+
46
+ **<n>. <box>**: <the reason, and whether the cause sits on the page or off it>.
47
+
48
+ <details>
49
+ <summary>Passed (P)</summary>
50
+
51
+ | Box | Proof |
52
+ | --------------------- | ------------------------------------------------------ |
53
+ | <n>. <box, shortened> | <the value read, and the evidence `<stem>` it matched> |
54
+
55
+ </details>
56
+
57
+ <details>
58
+ <summary>Sweep and guessed wording</summary>
39
59
 
40
60
  **Sweep**
41
61
 
@@ -45,26 +65,42 @@ P passed, F failed, E need eyes, N not driven. Drove <address> at <short-sha>, r
45
65
 
46
66
  - Box <n>: <what the line left out and what the pass assumed>
47
67
 
68
+ </details>
69
+
48
70
  🤖 Driven by Claude Code
49
71
 
50
72
  <!-- review-ui: head=<head> -->
51
73
  ```
52
74
 
53
- The table has two columns and each Box cell opens on its verdict in bold with a period: `**Pass.**`, `**Fail.**`, `**Needs eyes.**`, or `**Not driven.**`. A column holding only those short words loses its width beside two long ones and GitHub breaks its header mid-word, so the verdict rides in the Box cell instead.
75
+ The body opens on its count line, in that order, so the operator reads what they owe before anything else. The second line carries the address, the head, and the renderer, and says the address was driven unverified when it came from the local rung or the launch.
76
+
77
+ Each section is dropped when it holds no box, along with its heading. Order is fixed: Failed, Needs your eyes, Not driven, then the folded blocks. A box takes exactly one section, chosen by its verdict, so a box that fails and also ends in `(taste)` appears once, under Failed.
78
+
79
+ A box in a section opens on its number and the box as written, in bold, with the reading beneath it in words. A failed box that rests on a frame shows that frame inline under its heading, the `![box <n>](link)` embed with the link exactly as `canon pr frames` returned it. It names the frame's path on the frames branch, which stays put while other pull requests' frames are dropped and carries this pass's stamp, so a later pass never changes the image. A failed box that rests on a read has no image.
80
+
81
+ A needs-eyes box shows its dark and light shots in a two-cell table ahead of anything else it carries, since the operator decides from them without opening the app. A cell is either the `link` Step 5 pushed or, for a theme the worker's evidence already shows, the evidence image at `https://github.com/<owner>/<repo>/blob/<head>/<path>?raw=true`, with `<path>` the matching state item's `path` off the evidence record. A box whose evidence holds one theme still renders both cells, one evidence link and one pushed link. A push that refused leaves its cell holding the words `not shown, <reason>`, so the table keeps its shape, and the box's line names what the frame showed. Never cite the local frame path, since a path under `.canon/` is a board identifier the label scan rejects.
54
82
 
55
- An Evidence cell resting on a frame holds words only, so the table's text columns keep the body's width. A frame Step 5 pushed goes in the Frames block below the table, one `Box <n>, <verdict>` label then the `![](<link>)` embed for each framed box, with the link exactly as `canon pr frames` returned it. It names the frame's path on the frames branch, which stays put while other pull requests' frames are dropped and carries this pass's stamp, so a later pass never changes the image.
83
+ The Passed block holds one row per passed box, with each row's measurement and the evidence stem it matched in backticks, so every reading stays one click away. A pass that cites a stem links it as the evidence image at the form above when the record holds the item. Omit the block when nothing passed.
56
84
 
57
- The row still says what the frame showed, so it reads without the image. A state the worker's evidence already shows names the evidence stem in backticks and embeds nothing, since the evidence comment carries that image. A push that refused leaves the words alone and names the reason. Omit the Frames block when no box pushed a frame. Never cite the local frame path, since a path under `.canon/` is a board identifier the label scan rejects.
85
+ Omit the Sweep block when it found nothing, and the Guessed wording block when every box named its route and width, and drop the folded block when both are empty. Quote page text in backticks as content, never as the pass's own words.
58
86
 
59
- A pass where every box needs eyes and nothing else is owed takes the closed heading:
87
+ A pass where every box passed is the heading, the count line, the folded Passed block, and the marker, with no empty headings:
60
88
 
61
89
  ```markdown
62
90
  ## UI review closed
63
91
 
64
- 0 passed, 0 failed, 3 need eyes, 0 not driven. Drove <address> at <short-sha>, rendered by <renderer>.
92
+ 0 failed · 0 need your eyes · 8 passed · 0 not driven
93
+ Driven at `<short-sha>` on <address>, rendered by <renderer>.
94
+
95
+ <details>
96
+ <summary>Passed (8)</summary>
97
+
98
+ ...
99
+
100
+ </details>
65
101
  ```
66
102
 
67
- Omit the Sweep block when it found nothing, and the Guessed wording block when every box named its route and width. Quote page text in backticks as content, never as the pass's own words.
103
+ The count line and the sections beneath it hold the same totals, and Step 10's printed line is the same four counts. A pass where every box needs eyes and nothing else is owed takes the closed heading.
68
104
 
69
105
  ## The marker
70
106
 
@@ -72,7 +108,7 @@ End every body on `<!-- review-ui: head=<head> -->`, carrying the full sha Step
72
108
 
73
109
  ## Posting
74
110
 
75
- Run `canon labels scan --body-file .canon/tmp/pr/review-ui/body-<number>-<short-sha>.md` against the body first, which applies the rule in `${CLAUDE_SKILL_DIR}/../../standards/publish.md`, since nothing gates a file under `.canon/tmp/` before the review-event run does. Fix the body and scan again on a finding. Then post it as a review rather than an issue comment, since the poll reads the UI family off reviews:
111
+ Run `canon labels scan --body-file .canon/tmp/pr/review-ui/body-<number>-<short-sha>.md` against the body first, which applies the rule in `${CLAUDE_SKILL_DIR}/../git-pr/references/publish.md`, since nothing gates a file under `.canon/tmp/` before the review-event run does. Fix the body and scan again on a finding. Then post it as a review rather than an issue comment, since the poll reads the UI family off reviews:
76
112
 
77
113
  Record the instant just before the post, in UTC, as `<posted-at>`. Step 9 keeps only review-event runs created after it, since the code review posts its own review on the same head and fires a run of the same gate.
78
114
 
@@ -80,6 +80,8 @@ Dispatch parts from what it checked in three ways. The gate and the worker each
80
80
  - Carry the dispatcher's `sessionId` in the launch prompt, since a worker owed two messages has no property on the roster that resolves a controller and a name goes stale inside the window it builds in
81
81
  - Report each self-dispatch and the row it fired against loudly enough to follow, since a person no longer watches the launch step happen
82
82
  - Stop dispatching once `## Run now` is empty or every row in it reads claimed, rather than waking again to fire on a board nobody is clearing
83
+ - Launch a planner or worker the operator will talk with from the talk shape, with the `-talk` suffix and the brief sentence that tells it so, since a cold launch retires the session the moment its delivery lands while the operator still wants the conversation
84
+ - Retire a talk session only on the operator's word and report it as held open until then, since its delivery says nothing about whether the conversation is over
83
85
  - Dispatch a pull request's review, first pass included, to `role-reviewer` on load or on executable content, a skill body included, and keep one cross-branch pass per wave here, since a warm pass holding a wave skims what a cold one reads
84
86
 
85
87
  ## Must not
@@ -105,6 +107,7 @@ Dispatch parts from what it checked in three ways. The gate and the worker each
105
107
  - Blocker only an operator can clear: record the row as untestable this pass and name the action owed, rather than re-measuring what no session can move
106
108
  - Branch check refuses, with no session registry or no repository resolved: treat the candidate as unverified and fall back to the human-launch line, rather than reading a check that could not run as a clear one
107
109
  - Candidate holds under the conflict check: leave the row ready and name the hold, rather than dispatching onto a contract still moving
110
+ - Talk session the client renamed after launch: find it by the `sessionId` recorded in the map, since the `-talk` suffix is display only and a rename can drop it
108
111
  - Operator states a cap for the session: honor it for that session alone, rather than treating it as a standing rule or writing the number into a file
109
112
 
110
113
  ## Out of scope