@erclx/canon 5.40.0 → 5.42.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 (187) hide show
  1. package/README.md +34 -17
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/references/adopted.md +34 -14
  4. package/claude/skills/auto-ship/REQUIREMENT.md +2 -0
  5. package/claude/skills/auto-ship/SKILL.md +28 -2
  6. package/claude/skills/auto-ship/references/draft-mark.md +32 -6
  7. package/claude/skills/auto-ship/references/failure-recovery.md +15 -13
  8. package/claude/skills/auto-ship/references/fan-in.md +59 -0
  9. package/claude/skills/auto-ship/references/tier-failures.md +31 -10
  10. package/claude/skills/auto-ship/references/ui-checklist.md +9 -4
  11. package/claude/skills/auto-ship/references/worktree-name.md +9 -3
  12. package/claude/skills/build-in-slices/references/adopted.md +35 -21
  13. package/claude/skills/canvas-board/references/canvas.md +17 -6
  14. package/claude/skills/canvas-board/references/design.md +26 -7
  15. package/claude/skills/code-craft/references/adopted.md +58 -20
  16. package/claude/skills/codebase-layout/references/adopted.md +49 -16
  17. package/claude/skills/codebase-layout/references/frontend.md +11 -4
  18. package/claude/skills/codebase-layout/references/tests.md +4 -2
  19. package/claude/skills/context-fold/references/classify.md +10 -15
  20. package/claude/skills/context-fold/references/completion-report.md +11 -9
  21. package/claude/skills/context-fold/references/context-refresh.md +26 -9
  22. package/claude/skills/context-fold/references/unusable-baseline.md +5 -3
  23. package/claude/skills/create-rule/REQUIREMENT.md +5 -0
  24. package/claude/skills/create-rule/SKILL.md +9 -1
  25. package/claude/skills/create-rule/references/checklist.md +26 -0
  26. package/claude/skills/create-rule/references/rule.md +98 -20
  27. package/claude/skills/create-skill/SKILL.md +1 -0
  28. package/claude/skills/create-skill/references/skill-disclosure.md +10 -2
  29. package/claude/skills/create-skill/references/skill-frontmatter.md +43 -0
  30. package/claude/skills/create-skill/references/skill-paths.md +7 -4
  31. package/claude/skills/create-skill/references/skill-practice.md +6 -3
  32. package/claude/skills/create-skill/references/skill-requirement.md +4 -2
  33. package/claude/skills/create-skill/references/skill.md +67 -44
  34. package/claude/skills/decision-escalate/REQUIREMENT.md +1 -1
  35. package/claude/skills/decision-escalate/SKILL.md +1 -2
  36. package/claude/skills/deploy-app/references/vercel.md +2 -1
  37. package/claude/skills/deprecation-migration/references/adopted.md +44 -25
  38. package/claude/skills/design-taste/references/adopted.md +9 -1
  39. package/claude/skills/design-taste/references/craft.md +3 -1
  40. package/claude/skills/design-taste/references/kinds.md +8 -7
  41. package/claude/skills/design-taste/references/preflight.md +12 -0
  42. package/claude/skills/design-taste/references/systems.md +7 -6
  43. package/claude/skills/design-taste/references/tells.md +24 -25
  44. package/claude/skills/design-taste/references/vocabulary.md +7 -5
  45. package/claude/skills/document-health/references/axes.md +54 -30
  46. package/claude/skills/draft-and-pick/references/live-arms.md +2 -2
  47. package/claude/skills/draft-doc/references/context.md +4 -3
  48. package/claude/skills/draft-doc/references/docs.md +6 -3
  49. package/claude/skills/draft-doc/references/readme/agent-facing.md +6 -0
  50. package/claude/skills/draft-doc/references/readme/application.md +8 -2
  51. package/claude/skills/draft-doc/references/readme/cli.md +5 -1
  52. package/claude/skills/draft-doc/references/readme/library.md +4 -0
  53. package/claude/skills/draft-doc/references/readme/plugin.md +9 -1
  54. package/claude/skills/draft-doc/references/readme.md +30 -13
  55. package/claude/skills/draft-doc/references/requirements.md +17 -4
  56. package/claude/skills/draft-doc/references/wiki-kind.md +20 -10
  57. package/claude/skills/draft-figure/references/architecture-set.md +7 -3
  58. package/claude/skills/draft-figure/references/figures.md +8 -6
  59. package/claude/skills/draft-figure/references/hand-drawn.md +3 -1
  60. package/claude/skills/draft-figure/references/mermaid-path.md +19 -9
  61. package/claude/skills/draft-figure/references/mermaid.md +10 -5
  62. package/claude/skills/draft-ready/references/ready.md +22 -15
  63. package/claude/skills/git-branch/REQUIREMENT.md +17 -6
  64. package/claude/skills/git-branch/SKILL.md +63 -7
  65. package/claude/skills/git-branch/references/branch.md +11 -1
  66. package/claude/skills/git-branch/references/slug.md +17 -5
  67. package/claude/skills/git-commit/references/commit.md +8 -0
  68. package/claude/skills/git-commit/references/versioning.md +23 -17
  69. package/claude/skills/git-followup/REQUIREMENT.md +1 -1
  70. package/claude/skills/git-followup/SKILL.md +5 -6
  71. package/claude/skills/git-issue/references/issue.md +29 -16
  72. package/claude/skills/git-pr/REQUIREMENT.md +1 -5
  73. package/claude/skills/git-pr/SKILL.md +11 -22
  74. package/claude/skills/git-pr/references/final-command.md +21 -8
  75. package/claude/skills/git-pr/references/labels.md +45 -28
  76. package/claude/skills/git-pr/references/pr.md +28 -9
  77. package/claude/skills/git-pr/references/publish.md +42 -28
  78. package/claude/skills/git-pr/references/task-number.md +20 -8
  79. package/claude/skills/git-ship/SKILL.md +1 -1
  80. package/claude/skills/plan-feature/references/batches.md +48 -1
  81. package/claude/skills/plan-feature/references/plan-lifecycle.md +10 -3
  82. package/claude/skills/plan-feature/references/plan.md +59 -20
  83. package/claude/skills/plan-groundwork/references/groundwork-spikes.md +4 -1
  84. package/claude/skills/plan-groundwork/references/groundwork.md +14 -16
  85. package/claude/skills/plan-intake/references/intake.md +29 -24
  86. package/claude/skills/plan-intake-answer/REQUIREMENT.md +1 -1
  87. package/claude/skills/plan-intake-answer/SKILL.md +1 -2
  88. package/claude/skills/pr-evidence/REQUIREMENT.md +40 -0
  89. package/claude/skills/pr-evidence/SKILL.md +53 -0
  90. package/claude/skills/pr-evidence/references/evidence.md +150 -0
  91. package/claude/skills/review-address/references/post-review-findings.md +10 -3
  92. package/claude/skills/review-address/references/rebase-conflicts.md +22 -7
  93. package/claude/skills/review-address/references/rest-reads.md +3 -1
  94. package/claude/skills/review-craft/references/adopted.md +35 -18
  95. package/claude/skills/review-craft/references/security.md +42 -7
  96. package/claude/skills/review-craft/references/ui.md +23 -8
  97. package/claude/skills/review-pr/SKILL.md +6 -6
  98. package/claude/skills/review-pr/references/body-variants.md +29 -9
  99. package/claude/skills/review-pr/references/close-out.md +20 -5
  100. package/claude/skills/review-pr/references/key-changes.md +27 -3
  101. package/claude/skills/review-pr/references/post.md +83 -31
  102. package/claude/skills/review-pr/references/scope.md +54 -19
  103. package/claude/skills/review-ui/SKILL.md +2 -0
  104. package/claude/skills/review-ui/references/close-out.md +49 -0
  105. package/claude/skills/review-ui/references/driving.md +24 -4
  106. package/claude/skills/review-ui/references/post.md +43 -13
  107. package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +67 -17
  108. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +81 -40
  109. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +52 -15
  110. package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +48 -26
  111. package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +12 -13
  112. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +72 -36
  113. package/claude/skills/role-orchestrator/references/orchestrator-output.md +12 -4
  114. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +64 -24
  115. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +80 -26
  116. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +41 -18
  117. package/claude/skills/role-orchestrator/references/orchestrator-resume.md +19 -7
  118. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +22 -12
  119. package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +15 -6
  120. package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +25 -7
  121. package/claude/skills/role-orchestrator/references/orchestrator-watch.md +34 -8
  122. package/claude/skills/role-orchestrator/scripts/poll.ts +16 -24
  123. package/claude/skills/role-worker/REQUIREMENT.md +2 -0
  124. package/claude/skills/role-worker/SKILL.md +17 -0
  125. package/claude/skills/search-craft/references/adopted.md +18 -6
  126. package/claude/skills/session-map/references/session.md +26 -7
  127. package/claude/skills/session-worktree/references/main-root-writes.md +25 -8
  128. package/claude/skills/systematic-debugging/references/adopted.md +30 -16
  129. package/claude/skills/target-check/references/domains.md +39 -11
  130. package/claude/skills/target-setup/references/detect.md +80 -65
  131. package/claude/skills/target-setup/references/indexes.md +8 -6
  132. package/claude/skills/task-board/references/board.md +86 -30
  133. package/claude/skills/task-board/references/decline.md +29 -13
  134. package/claude/skills/task-board/references/tasks-decline.md +12 -5
  135. package/claude/skills/task-board/references/tasks.md +65 -33
  136. package/claude/skills/task-complete/references/archive.md +41 -17
  137. package/claude/skills/task-complete/references/receipt-sweep.md +23 -9
  138. package/claude/skills/task-complete/references/unusable-baseline.md +10 -3
  139. package/claude/skills/teach-workspace/REQUIREMENT.md +1 -1
  140. package/claude/skills/teach-workspace/SKILL.md +1 -2
  141. package/claude/skills/teach-workspace/references/glossary.md +6 -2
  142. package/claude/skills/teach-workspace/references/lesson-build.md +44 -16
  143. package/claude/skills/teach-workspace/references/lesson-craft.md +12 -7
  144. package/claude/skills/teach-workspace/references/pedagogy.md +6 -3
  145. package/claude/skills/teach-workspace/references/promotion-fold.md +17 -11
  146. package/claude/skills/teach-workspace/references/promotion.md +31 -8
  147. package/claude/skills/teach-workspace/references/revisit.md +8 -2
  148. package/claude/skills/teach-workspace/references/teach.md +17 -9
  149. package/claude/skills/test-craft/references/adopted.md +48 -14
  150. package/claude/skills/test-craft/references/component.md +7 -4
  151. package/claude/skills/test-craft/references/e2e.md +25 -7
  152. package/claude/skills/test-craft/references/unit.md +3 -1
  153. package/claude/skills/test-first/references/adopted.md +12 -4
  154. package/claude/skills/ui-checklist/REQUIREMENT.md +1 -1
  155. package/claude/skills/ui-checklist/SKILL.md +1 -1
  156. package/claude/skills/video-craft/references/adopted.md +62 -18
  157. package/claude/skills/write-human/references/adopted.md +21 -8
  158. package/docs/workflow/parallel-features.md +2 -1
  159. package/docs/workflow/skill-map.md +2 -1
  160. package/package.json +1 -1
  161. package/src/claude/cases/git.ts +4 -0
  162. package/src/claude/cases/workflow.ts +5 -0
  163. package/src/commands/pr.ts +174 -37
  164. package/src/commands/records.ts +11 -3
  165. package/src/context/shape-gated.ts +1 -0
  166. package/src/markdown/scope.ts +150 -2
  167. package/src/pr/frames.ts +53 -9
  168. package/src/pr/lift.ts +6 -3
  169. package/src/pr/review-state.ts +182 -0
  170. package/src/records/validate.ts +1 -0
  171. package/src/tasks/answers.ts +79 -2
  172. package/src/tasks/fan-in.ts +176 -0
  173. package/src/tasks/stack.ts +1 -1
  174. package/tooling/astro/reference.md +80 -39
  175. package/tooling/base/reference.md +63 -32
  176. package/tooling/base/seeds/canon/context/ci.md +7 -15
  177. package/tooling/base/seeds/canon/context/development.md +14 -14
  178. package/tooling/claude/reference.md +40 -22
  179. package/tooling/cloudflare/reference.md +31 -12
  180. package/tooling/go/reference.md +47 -23
  181. package/tooling/nextjs/reference.md +52 -19
  182. package/tooling/php/reference.md +35 -16
  183. package/tooling/python/reference.md +47 -30
  184. package/tooling/shell/reference.md +6 -5
  185. package/tooling/vite-react/reference.md +49 -21
  186. package/tooling/web/reference.md +130 -70
  187. package/claude/skills/git-pr/references/evidence.md +0 -124
@@ -44,6 +44,7 @@ import {
44
44
  plural,
45
45
  } from '@/cli/ui'
46
46
  import { currentWorktreeRoot, mainWorktreeRoot } from '@/git/worktree'
47
+ import { fanInFindings } from '@/tasks/fan-in'
47
48
  import { readLivePlans, stackFindings } from '@/tasks/stack'
48
49
 
49
50
  /** Returned when a record carries a finding, which is the gating result. */
@@ -742,7 +743,7 @@ async function runValidate(
742
743
  }
743
744
 
744
745
  /**
745
- * Appends the one plans finding that reads across files. It sits in the command
746
+ * Appends the plans findings that read across files. They sit in the command
746
747
  * layer because `src/records/` sits below `src/tasks/`.
747
748
  */
748
749
  async function validateWithStacks(
@@ -752,9 +753,16 @@ async function validateWithStacks(
752
753
  const outcome = await validateRecords(root, kind)
753
754
  if (!outcome.ok || kind !== 'plans') return outcome
754
755
 
755
- const stacked = stackFindings(await readLivePlans(root))
756
+ const live = await readLivePlans(root)
756
757
 
757
- return { ...outcome, findings: [...outcome.findings, ...stacked] }
758
+ return {
759
+ ...outcome,
760
+ findings: [
761
+ ...outcome.findings,
762
+ ...stackFindings(live),
763
+ ...fanInFindings(live),
764
+ ],
765
+ }
758
766
  }
759
767
 
760
768
  function report(
@@ -19,6 +19,7 @@ export const SHAPE_GATED_FOLDERS: readonly string[] = [
19
19
  'canon/context/features/',
20
20
  'canon/context/surfaces/',
21
21
  'canon/context/workflow/',
22
+ 'tooling/base/seeds/',
22
23
  ]
23
24
 
24
25
  /** Whether a repository-relative entry path sits under a gated prefix. */
@@ -42,41 +42,113 @@ export const GATED_PATHS: readonly string[] = [
42
42
  'canon/context/context-model.md',
43
43
  'canon/context/index.md',
44
44
  'claude/skills/api-design/SKILL.md',
45
+ 'claude/skills/api-design/references/adopted.md',
45
46
  'claude/skills/auto-ship/SKILL.md',
47
+ 'claude/skills/auto-ship/references/draft-mark.md',
48
+ 'claude/skills/auto-ship/references/failure-recovery.md',
49
+ 'claude/skills/auto-ship/references/fan-in.md',
50
+ 'claude/skills/auto-ship/references/tier-failures.md',
51
+ 'claude/skills/auto-ship/references/ui-checklist.md',
52
+ 'claude/skills/auto-ship/references/verb-absent.md',
53
+ 'claude/skills/auto-ship/references/worktree-name.md',
46
54
  'claude/skills/backlog-triage/SKILL.md',
47
55
  'claude/skills/backlog-triage/references/apply.md',
48
56
  'claude/skills/build-in-slices/SKILL.md',
57
+ 'claude/skills/build-in-slices/references/adopted.md',
49
58
  'claude/skills/canon-cli/SKILL.md',
50
59
  'claude/skills/canon-feedback-triage/SKILL.md',
51
60
  'claude/skills/canon-feedback/SKILL.md',
61
+ 'claude/skills/canon-feedback/references/template.md',
52
62
  'claude/skills/canvas-board/SKILL.md',
63
+ 'claude/skills/canvas-board/references/canvas.md',
64
+ 'claude/skills/canvas-board/references/design.md',
53
65
  'claude/skills/ci-workflow/SKILL.md',
66
+ 'claude/skills/ci-workflow/references/workflows.md',
54
67
  'claude/skills/code-craft/SKILL.md',
68
+ 'claude/skills/code-craft/references/adopted.md',
55
69
  'claude/skills/codebase-layout/SKILL.md',
70
+ 'claude/skills/codebase-layout/references/adopted.md',
71
+ 'claude/skills/codebase-layout/references/frontend.md',
72
+ 'claude/skills/codebase-layout/references/tests.md',
56
73
  'claude/skills/context-fold/SKILL.md',
74
+ 'claude/skills/context-fold/references/classify.md',
75
+ 'claude/skills/context-fold/references/completion-report.md',
76
+ 'claude/skills/context-fold/references/context-refresh.md',
77
+ 'claude/skills/context-fold/references/context.md',
78
+ 'claude/skills/context-fold/references/unusable-baseline.md',
57
79
  'claude/skills/create-rule/SKILL.md',
80
+ 'claude/skills/create-rule/references/rule.md',
58
81
  'claude/skills/create-skill/SKILL.md',
82
+ 'claude/skills/create-skill/references/skill-disclosure.md',
83
+ 'claude/skills/create-skill/references/skill-frontmatter.md',
84
+ 'claude/skills/create-skill/references/skill-paths.md',
85
+ 'claude/skills/create-skill/references/skill-practice.md',
86
+ 'claude/skills/create-skill/references/skill-reference.md',
87
+ 'claude/skills/create-skill/references/skill-requirement.md',
88
+ 'claude/skills/create-skill/references/skill.md',
59
89
  'claude/skills/decision-escalate/SKILL.md',
60
90
  'claude/skills/deploy-app/SKILL.md',
91
+ 'claude/skills/deploy-app/references/cloudflare.md',
92
+ 'claude/skills/deploy-app/references/vercel.md',
61
93
  'claude/skills/deprecation-migration/SKILL.md',
94
+ 'claude/skills/deprecation-migration/references/adopted.md',
62
95
  'claude/skills/design-taste/SKILL.md',
96
+ 'claude/skills/design-taste/references/adopted.md',
97
+ 'claude/skills/design-taste/references/craft.md',
98
+ 'claude/skills/design-taste/references/kinds.md',
99
+ 'claude/skills/design-taste/references/preflight.md',
100
+ 'claude/skills/design-taste/references/systems.md',
101
+ 'claude/skills/design-taste/references/tells.md',
102
+ 'claude/skills/design-taste/references/vocabulary.md',
63
103
  'claude/skills/docs-sync/SKILL.md',
104
+ 'claude/skills/docs-sync/references/docs.md',
105
+ 'claude/skills/docs-sync/references/readme.md',
64
106
  'claude/skills/document-health/SKILL.md',
107
+ 'claude/skills/document-health/references/axes.md',
65
108
  'claude/skills/draft-and-pick/SKILL.md',
109
+ 'claude/skills/draft-and-pick/references/live-arms.md',
66
110
  'claude/skills/draft-doc/SKILL.md',
111
+ 'claude/skills/draft-doc/references/context.md',
112
+ 'claude/skills/draft-doc/references/docs.md',
113
+ 'claude/skills/draft-doc/references/readme.md',
114
+ 'claude/skills/draft-doc/references/readme/agent-facing.md',
115
+ 'claude/skills/draft-doc/references/readme/application.md',
116
+ 'claude/skills/draft-doc/references/readme/cli.md',
117
+ 'claude/skills/draft-doc/references/readme/library.md',
118
+ 'claude/skills/draft-doc/references/readme/plugin.md',
119
+ 'claude/skills/draft-doc/references/requirements.md',
120
+ 'claude/skills/draft-doc/references/wiki-kind.md',
121
+ 'claude/skills/draft-doc/references/wiki.md',
67
122
  'claude/skills/draft-figure/SKILL.md',
123
+ 'claude/skills/draft-figure/references/architecture-set.md',
124
+ 'claude/skills/draft-figure/references/figures.md',
125
+ 'claude/skills/draft-figure/references/hand-drawn.md',
126
+ 'claude/skills/draft-figure/references/mermaid-path.md',
127
+ 'claude/skills/draft-figure/references/mermaid.md',
68
128
  'claude/skills/draft-identity/SKILL.md',
69
129
  'claude/skills/draft-identity/references/card-route.md',
70
130
  'claude/skills/draft-ready/SKILL.md',
131
+ 'claude/skills/draft-ready/references/assembly.md',
132
+ 'claude/skills/draft-ready/references/ready.md',
71
133
  'claude/skills/draft-screencast/SKILL.md',
72
134
  'claude/skills/draft-screencast/references/beats-template.md',
73
135
  'claude/skills/draft-slides/SKILL.md',
74
136
  'claude/skills/draft-slides/references/deck.md',
75
137
  'claude/skills/git-branch/SKILL.md',
138
+ 'claude/skills/git-branch/references/branch.md',
139
+ 'claude/skills/git-branch/references/slug.md',
76
140
  'claude/skills/git-commit/SKILL.md',
141
+ 'claude/skills/git-commit/references/commit.md',
142
+ 'claude/skills/git-commit/references/versioning.md',
77
143
  'claude/skills/git-followup/SKILL.md',
78
144
  'claude/skills/git-issue/SKILL.md',
145
+ 'claude/skills/git-issue/references/issue.md',
79
146
  'claude/skills/git-pr/SKILL.md',
147
+ 'claude/skills/git-pr/references/final-command.md',
148
+ 'claude/skills/git-pr/references/labels.md',
149
+ 'claude/skills/git-pr/references/pr.md',
150
+ 'claude/skills/git-pr/references/publish.md',
151
+ 'claude/skills/git-pr/references/task-number.md',
80
152
  'claude/skills/git-ship/SKILL.md',
81
153
  'claude/skills/git-ship/references/ci-watch.md',
82
154
  'claude/skills/git-split/SKILL.md',
@@ -86,48 +158,124 @@ export const GATED_PATHS: readonly string[] = [
86
158
  'claude/skills/git-worktree/SKILL.md',
87
159
  'claude/skills/git-worktree/references/cleanup.md',
88
160
  'claude/skills/markdown-craft/SKILL.md',
161
+ 'claude/skills/markdown-craft/references/markdown.md',
89
162
  'claude/skills/plan-feature/SKILL.md',
90
163
  'claude/skills/plan-feature/references/batches.md',
164
+ 'claude/skills/plan-feature/references/plan-lifecycle.md',
165
+ 'claude/skills/plan-feature/references/plan.md',
91
166
  'claude/skills/plan-groundwork/SKILL.md',
167
+ 'claude/skills/plan-groundwork/references/groundwork-spikes.md',
168
+ 'claude/skills/plan-groundwork/references/groundwork.md',
92
169
  'claude/skills/plan-groundwork/references/output.md',
93
170
  'claude/skills/plan-intake-answer/SKILL.md',
94
171
  'claude/skills/plan-intake/SKILL.md',
172
+ 'claude/skills/plan-intake/references/intake.md',
173
+ 'claude/skills/pr-evidence/SKILL.md',
174
+ 'claude/skills/pr-evidence/references/evidence.md',
95
175
  'claude/skills/read-frames/SKILL.md',
96
176
  'claude/skills/record-screencast/SKILL.md',
97
177
  'claude/skills/review-address/SKILL.md',
178
+ 'claude/skills/review-address/references/post-review-findings.md',
179
+ 'claude/skills/review-address/references/rebase-conflicts.md',
98
180
  'claude/skills/review-address/references/rebase-only.md',
181
+ 'claude/skills/review-address/references/rest-reads.md',
99
182
  'claude/skills/review-craft/SKILL.md',
183
+ 'claude/skills/review-craft/references/adopted.md',
184
+ 'claude/skills/review-craft/references/security.md',
185
+ 'claude/skills/review-craft/references/ui.md',
100
186
  'claude/skills/review-pr/SKILL.md',
187
+ 'claude/skills/review-pr/references/body-variants.md',
188
+ 'claude/skills/review-pr/references/close-out.md',
189
+ 'claude/skills/review-pr/references/key-changes.md',
190
+ 'claude/skills/review-pr/references/post.md',
191
+ 'claude/skills/review-pr/references/scope.md',
101
192
  'claude/skills/review-ui/SKILL.md',
193
+ 'claude/skills/review-ui/references/close-out.md',
194
+ 'claude/skills/review-ui/references/driving.md',
102
195
  'claude/skills/review-ui/references/hosted-preview.md',
196
+ 'claude/skills/review-ui/references/post.md',
197
+ 'claude/skills/review-ui/references/tick.md',
103
198
  'claude/skills/role-orchestrator/SKILL.md',
199
+ 'claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md',
200
+ 'claude/skills/role-orchestrator/references/orchestrator-dispatch.md',
104
201
  'claude/skills/role-orchestrator/references/orchestrator-filing.md',
202
+ 'claude/skills/role-orchestrator/references/orchestrator-handback.md',
203
+ 'claude/skills/role-orchestrator/references/orchestrator-handoff.md',
204
+ 'claude/skills/role-orchestrator/references/orchestrator-inbound.md',
205
+ 'claude/skills/role-orchestrator/references/orchestrator-launch.md',
206
+ 'claude/skills/role-orchestrator/references/orchestrator-output.md',
207
+ 'claude/skills/role-orchestrator/references/orchestrator-parked.md',
208
+ 'claude/skills/role-orchestrator/references/orchestrator-poll.md',
209
+ 'claude/skills/role-orchestrator/references/orchestrator-refill.md',
210
+ 'claude/skills/role-orchestrator/references/orchestrator-resume.md',
211
+ 'claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md',
212
+ 'claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md',
213
+ 'claude/skills/role-orchestrator/references/orchestrator-sweep.md',
214
+ 'claude/skills/role-orchestrator/references/orchestrator-watch.md',
105
215
  'claude/skills/role-planner/SKILL.md',
106
216
  'claude/skills/role-reviewer/SKILL.md',
107
217
  'claude/skills/role-worker/SKILL.md',
108
218
  'claude/skills/search-craft/SKILL.md',
219
+ 'claude/skills/search-craft/references/adopted.md',
109
220
  'claude/skills/session-compact/SKILL.md',
221
+ 'claude/skills/session-compact/references/handoff-note.md',
110
222
  'claude/skills/session-map/SKILL.md',
223
+ 'claude/skills/session-map/references/session.md',
111
224
  'claude/skills/session-relay/SKILL.md',
112
225
  'claude/skills/session-resume/SKILL.md',
113
226
  'claude/skills/session-worktree/SKILL.md',
227
+ 'claude/skills/session-worktree/references/install-command.md',
228
+ 'claude/skills/session-worktree/references/main-root-writes.md',
114
229
  'claude/skills/systematic-debugging/SKILL.md',
230
+ 'claude/skills/systematic-debugging/references/adopted.md',
115
231
  'claude/skills/target-check/SKILL.md',
232
+ 'claude/skills/target-check/references/domains.md',
116
233
  'claude/skills/target-setup/SKILL.md',
234
+ 'claude/skills/target-setup/references/detect.md',
235
+ 'claude/skills/target-setup/references/indexes.md',
236
+ 'claude/skills/target-setup/references/verify.md',
117
237
  'claude/skills/task-board/SKILL.md',
238
+ 'claude/skills/task-board/references/board.md',
118
239
  'claude/skills/task-board/references/create-report.md',
240
+ 'claude/skills/task-board/references/decline.md',
241
+ 'claude/skills/task-board/references/tasks-decline.md',
242
+ 'claude/skills/task-board/references/tasks.md',
119
243
  'claude/skills/task-complete/SKILL.md',
244
+ 'claude/skills/task-complete/references/archive.md',
245
+ 'claude/skills/task-complete/references/receipt-sweep.md',
246
+ 'claude/skills/task-complete/references/unusable-baseline.md',
120
247
  'claude/skills/teach-workspace/SKILL.md',
248
+ 'claude/skills/teach-workspace/references/glossary.md',
249
+ 'claude/skills/teach-workspace/references/lesson-build.md',
250
+ 'claude/skills/teach-workspace/references/lesson-craft.md',
251
+ 'claude/skills/teach-workspace/references/pedagogy.md',
252
+ 'claude/skills/teach-workspace/references/promotion-fold.md',
253
+ 'claude/skills/teach-workspace/references/promotion.md',
254
+ 'claude/skills/teach-workspace/references/revisit.md',
255
+ 'claude/skills/teach-workspace/references/teach.md',
121
256
  'claude/skills/test-craft/SKILL.md',
257
+ 'claude/skills/test-craft/references/adopted.md',
258
+ 'claude/skills/test-craft/references/component.md',
259
+ 'claude/skills/test-craft/references/e2e.md',
260
+ 'claude/skills/test-craft/references/unit.md',
122
261
  'claude/skills/test-first/SKILL.md',
262
+ 'claude/skills/test-first/references/adopted.md',
123
263
  'claude/skills/ui-checklist/SKILL.md',
124
264
  'claude/skills/ux-audit/SKILL.md',
125
265
  'claude/skills/ux-measure/SKILL.md',
126
266
  'claude/skills/video-craft/SKILL.md',
267
+ 'claude/skills/video-craft/references/adopted.md',
127
268
  'claude/skills/write-human/SKILL.md',
269
+ 'claude/skills/write-human/references/adopted.md',
270
+ 'claude/skills/write-human/references/density.md',
271
+ 'claude/skills/write-human/references/machine-tells.md',
128
272
  'claude/skills/youtube-transcripts/SKILL.md',
129
273
  '.claude/skills/internal-',
130
274
  'docs/',
275
+ 'tooling/',
276
+ 'wiki/',
277
+ 'README.md',
278
+ 'CONTRIBUTING.md',
131
279
  ]
132
280
 
133
281
  /**
@@ -136,8 +284,8 @@ export const GATED_PATHS: readonly string[] = [
136
284
  * entries, the docs pages, and the skill bodies and references through a stage
137
285
  * of its own before this measure existed, and waiting on `GATED_PATHS` would
138
286
  * reopen that hole. The seeds, the stack references, the wiki, and the root
139
- * guides were never held to it, and seven runs in the stack references would
140
- * fail the push on the day this lands, so those wait for their batch.
287
+ * guides were never held to it, so they joined `GATED_PATHS` only once their
288
+ * rewrite brought them to zero.
141
289
  */
142
290
  const ALWAYS_GATED: readonly DensityKind[] = ['bullet-run']
143
291
 
package/src/pr/frames.ts CHANGED
@@ -19,6 +19,9 @@ const PASS_PATTERN = /^\d{8}T\d{6}Z$/
19
19
  /** How many times an add rebuilds on a tip another writer moved. */
20
20
  const ADD_ATTEMPTS = 3
21
21
 
22
+ /** One half of a reference address. It opens on a letter or digit and holds no separator, so `..` and `/` cannot survive it. */
23
+ const REFERENCE_PART = /^[A-Za-z0-9][A-Za-z0-9._-]*$/
24
+
22
25
  const FOLDER_PATTERN = /^pr-(\d+)$/
23
26
 
24
27
  const DAY_MS = 86_400_000
@@ -77,7 +80,8 @@ export interface FrameInput {
77
80
  readonly number: number
78
81
  readonly head: string
79
82
  readonly pass: string
80
- readonly box: number
83
+ readonly box?: number
84
+ readonly reference?: string
81
85
  readonly theme?: FrameTheme
82
86
  readonly bytes: Uint8Array
83
87
  }
@@ -158,6 +162,39 @@ export function frameLink(repo: string, path: string): string {
158
162
  return `https://github.com/${repo}/blob/${FRAMES_BRANCH}/${path}?raw=true`
159
163
  }
160
164
 
165
+ /**
166
+ * The file-name slug of a `<page>/<frame>` address, or undefined when the
167
+ * address would not round-trip. Each half must open on a letter or digit and
168
+ * hold only letters, digits, dots, underscores, and dashes, and neither may
169
+ * carry `--`, so the joined slug reads back as the same two names and a path
170
+ * never gains a directory or escapes the pass folder.
171
+ */
172
+ export function referenceSlug(address: string): string | undefined {
173
+ const parts = address.split('/')
174
+ if (parts.length !== 2) return undefined
175
+ const [page, frame] = parts as [string, string]
176
+ const valid = [page, frame].every(
177
+ (part) => REFERENCE_PART.test(part) && !part.includes('--'),
178
+ )
179
+ return valid ? `${page}--${frame}` : undefined
180
+ }
181
+
182
+ /**
183
+ * Where a design reference capture lands, beside the evidence frames of one
184
+ * pass. The slug repeats for one reference, so a second capture in a pass
185
+ * overwrites only itself. Undefined when the address has no valid slug.
186
+ */
187
+ export function referencePath(
188
+ number: number,
189
+ head: string,
190
+ pass: string,
191
+ address: string,
192
+ ): string | undefined {
193
+ const slug = referenceSlug(address)
194
+ if (slug === undefined) return undefined
195
+ return `pr-${number}/${head.slice(0, SHORT_HEAD_LENGTH)}/${pass}/ref-${slug}.png`
196
+ }
197
+
161
198
  /** The frame's bytes, or undefined when the file is absent, empty, or not a PNG. */
162
199
  export async function readFrame(path: string): Promise<Uint8Array | undefined> {
163
200
  const bytes = await readFile(path).catch(() => undefined)
@@ -179,17 +216,24 @@ export async function pushFrame(
179
216
  api: FramesApi,
180
217
  input: FrameInput,
181
218
  ): Promise<PushResult> {
182
- const path = framePath(
183
- input.number,
184
- input.head,
185
- input.pass,
186
- input.box,
187
- input.theme,
188
- )
219
+ const path =
220
+ input.reference === undefined
221
+ ? framePath(
222
+ input.number,
223
+ input.head,
224
+ input.pass,
225
+ input.box ?? 0,
226
+ input.theme,
227
+ )
228
+ : referencePath(input.number, input.head, input.pass, input.reference)
229
+ if (path === undefined) return { kind: 'refused', reason: 'push-failed' }
189
230
  const blob = await api.createBlob(Buffer.from(input.bytes).toString('base64'))
190
231
  if (blob === undefined) return { kind: 'refused', reason: 'push-failed' }
191
232
  const entry: TreeEntry = { path, mode: '100644', type: 'blob', sha: blob }
192
- const message = `chore(frames): add box ${input.box} of #${input.number}`
233
+ const message =
234
+ input.reference === undefined
235
+ ? `chore(frames): add box ${input.box} of #${input.number}`
236
+ : `chore(frames): add reference ${input.reference} of #${input.number}`
193
237
 
194
238
  for (let attempt = 0; attempt < ADD_ATTEMPTS; attempt += 1) {
195
239
  const tip = await api.readTip()
package/src/pr/lift.ts CHANGED
@@ -144,7 +144,10 @@ function fail(gate: GateName, reason: LiftRefusal): GateResult {
144
144
  * since the commit it covers is a stamp GitHub wrote at submission, which the
145
145
  * marker exists to replace.
146
146
  */
147
- function reviewGate(reviews: readonly ReviewRow[], head: string): GateResult {
147
+ export function reviewGate(
148
+ reviews: readonly ReviewRow[],
149
+ head: string,
150
+ ): GateResult {
148
151
  const scope = resolveReviewScope({ reviews })
149
152
  if (scope.state === 'none') return fail('review', 'review-missing')
150
153
  if (scope.state === 'open') return fail('review', 'review-open')
@@ -175,8 +178,8 @@ function carried(gates: readonly GateResult[]): {
175
178
  * checklist the reviewer drove as true as it was. A close that is not an
176
179
  * ancestor, which a force-push or a rebase leaves, never carries.
177
180
  */
178
- async function uiGate(
179
- api: LiftApi,
181
+ export async function uiGate(
182
+ api: Pick<LiftApi, 'readChanged'>,
180
183
  painted: PaintedPatterns,
181
184
  reviews: readonly ReviewRow[],
182
185
  head: string,
@@ -0,0 +1,182 @@
1
+ import {
2
+ CHECKS_POLL_MS,
3
+ type GateResult,
4
+ type LiftApi,
5
+ type LiftRefusal,
6
+ reviewGate,
7
+ uiGate,
8
+ } from '@/pr/lift'
9
+ import { DEFAULT_PAINTED, type PaintedPatterns } from '@/pr/painted'
10
+ import {
11
+ coversHead,
12
+ type ReviewRow,
13
+ type ReviewScope,
14
+ resolveReviewScope,
15
+ resolveUiScope,
16
+ type UiScope,
17
+ } from '@/pr/review-scope'
18
+
19
+ /**
20
+ * The reads the verb makes, a subset of the lift's so a worker waiting here and
21
+ * a reviewer lifting read one answer. A read that failed returns undefined, and
22
+ * the verb refuses `gh-failed` rather than reading absence as no pass.
23
+ */
24
+ export type ReviewStateApi = Pick<
25
+ LiftApi,
26
+ 'readReviews' | 'readEvidence' | 'readChanged' | 'sleep' | 'now'
27
+ > & {
28
+ /** The branch tip from the remote, which is what a pass has to cover. */
29
+ readTip(): Promise<string | undefined>
30
+ }
31
+
32
+ /** One family's standing at the tip. */
33
+ export interface FamilyState {
34
+ /** The lift's refusal for this family, absent when its gate passes. */
35
+ readonly reason?: LiftRefusal
36
+ /**
37
+ * Whether a pass covers the tip, which is what a waiting worker reads. True
38
+ * when the gate passes, or when the newest pass, open or unmarked, names the
39
+ * tip. The reason alone cannot answer it, since the lift reads an open pass
40
+ * before it reads currency.
41
+ */
42
+ readonly isCovered: boolean
43
+ }
44
+
45
+ export interface UiState extends UiScope, FamilyState {
46
+ /** Whether the evidence comment carries a checklist, which binds the family. */
47
+ readonly isBound: boolean
48
+ readonly carriedFrom?: string
49
+ readonly paintedPaths?: readonly string[]
50
+ }
51
+
52
+ export interface ReviewStateRecord {
53
+ readonly head: string
54
+ readonly scope: ReviewScope
55
+ readonly review: FamilyState
56
+ readonly ui: UiState
57
+ }
58
+
59
+ export type ReviewStateRead =
60
+ | { readonly kind: 'read'; readonly record: ReviewStateRecord }
61
+ | { readonly kind: 'refused'; readonly reason: 'gh-failed' }
62
+
63
+ export type ReviewWait =
64
+ | {
65
+ readonly kind: 'read'
66
+ readonly reason: 'covered' | 'timeout'
67
+ readonly record: ReviewStateRecord
68
+ readonly waitedSeconds: number
69
+ }
70
+ | {
71
+ readonly kind: 'refused'
72
+ readonly reason: 'gh-failed'
73
+ readonly waitedSeconds: number
74
+ }
75
+
76
+ export interface WaitOptions {
77
+ readonly waitSeconds: number
78
+ readonly pollMs?: number
79
+ readonly painted?: PaintedPatterns
80
+ }
81
+
82
+ function familyOf(gate: GateResult, commit: string | undefined, tip: string) {
83
+ return {
84
+ ...(gate.reason !== undefined && { reason: gate.reason }),
85
+ isCovered: gate.isOk || (commit !== undefined && coversHead(commit, tip)),
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Both families at the tip through the lift's own gates, so a worker and the
91
+ * lift never disagree on a reason. The evidence is read by the caller, since a
92
+ * wait reads it once rather than once per poll.
93
+ */
94
+ async function readAt(
95
+ api: ReviewStateApi,
96
+ hasChecklist: boolean,
97
+ painted: PaintedPatterns,
98
+ ): Promise<ReviewStateRead> {
99
+ const [tip, reviews] = await Promise.all([api.readTip(), api.readReviews()])
100
+ if (tip === undefined || reviews === undefined) {
101
+ return { kind: 'refused', reason: 'gh-failed' }
102
+ }
103
+
104
+ const listing: { reviews: readonly ReviewRow[] } = { reviews }
105
+ const scope = resolveReviewScope(listing)
106
+ const uiScope = resolveUiScope(listing)
107
+ const ui = await uiGate(api, painted, reviews, tip, hasChecklist)
108
+ if (ui.reason === 'gh-failed') return { kind: 'refused', reason: 'gh-failed' }
109
+
110
+ return {
111
+ kind: 'read',
112
+ record: {
113
+ head: tip,
114
+ scope,
115
+ review: familyOf(reviewGate(reviews, tip), scope.commit, tip),
116
+ ui: {
117
+ ...uiScope,
118
+ ...familyOf(ui, uiScope.commit, tip),
119
+ isBound: hasChecklist,
120
+ ...(ui.carriedFrom !== undefined && { carriedFrom: ui.carriedFrom }),
121
+ ...(ui.paintedPaths !== undefined && { paintedPaths: ui.paintedPaths }),
122
+ },
123
+ },
124
+ }
125
+ }
126
+
127
+ export async function readReviewState(
128
+ api: ReviewStateApi,
129
+ painted: PaintedPatterns = DEFAULT_PAINTED,
130
+ ): Promise<ReviewStateRead> {
131
+ const evidence = await api.readEvidence()
132
+ if (evidence === undefined) return { kind: 'refused', reason: 'gh-failed' }
133
+ return readAt(api, evidence.hasChecklist, painted)
134
+ }
135
+
136
+ /**
137
+ * Holds until every bound family has a pass covering the tip, or the deadline
138
+ * passes. The tip is read again every poll, so a push during the wait moves
139
+ * what a pass has to cover. The evidence is read once, since a checklist
140
+ * appearing mid-wait is a ship-chain write the waiting worker made before it
141
+ * started.
142
+ */
143
+ export async function waitForReviews(
144
+ api: ReviewStateApi,
145
+ opts: WaitOptions,
146
+ ): Promise<ReviewWait> {
147
+ const pollMs = opts.pollMs ?? CHECKS_POLL_MS
148
+ const painted = opts.painted ?? DEFAULT_PAINTED
149
+ const started = api.now()
150
+ const deadline = started + opts.waitSeconds * 1000
151
+ const waited = () => Math.round((api.now() - started) / 1000)
152
+
153
+ const evidence = await api.readEvidence()
154
+ if (evidence === undefined) {
155
+ return { kind: 'refused', reason: 'gh-failed', waitedSeconds: waited() }
156
+ }
157
+
158
+ for (;;) {
159
+ const read = await readAt(api, evidence.hasChecklist, painted)
160
+ if (read.kind === 'refused') {
161
+ return { ...read, waitedSeconds: waited() }
162
+ }
163
+ const { record } = read
164
+ if (record.review.isCovered && record.ui.isCovered) {
165
+ return {
166
+ kind: 'read',
167
+ reason: 'covered',
168
+ record,
169
+ waitedSeconds: waited(),
170
+ }
171
+ }
172
+ if (api.now() + pollMs > deadline) {
173
+ return {
174
+ kind: 'read',
175
+ reason: 'timeout',
176
+ record,
177
+ waitedSeconds: waited(),
178
+ }
179
+ }
180
+ await api.sleep(pollMs)
181
+ }
182
+ }
@@ -48,6 +48,7 @@ export const FINDING_KINDS = [
48
48
  'batch-unsplit',
49
49
  'source-unquoted',
50
50
  'stack-foldable',
51
+ 'fan-in-unmatched',
51
52
  ] as const
52
53
 
53
54
  export type FindingKind = (typeof FINDING_KINDS)[number]