@humanbased/crosscheck 1.2.0-beta.80 → 1.2.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 (301) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +388 -149
  3. package/README.zh.md +1 -1
  4. package/crosscheck.config.example.yml +2 -63
  5. package/dist/__tests__/codex.test.js +27 -14
  6. package/dist/__tests__/codex.test.js.map +1 -1
  7. package/dist/__tests__/comment-bodies.test.js +1 -49
  8. package/dist/__tests__/comment-bodies.test.js.map +1 -1
  9. package/dist/__tests__/conflict-resolve.test.js +1 -44
  10. package/dist/__tests__/conflict-resolve.test.js.map +1 -1
  11. package/dist/__tests__/fix.test.js +0 -33
  12. package/dist/__tests__/fix.test.js.map +1 -1
  13. package/dist/__tests__/onboard-preservation.test.js +0 -5
  14. package/dist/__tests__/onboard-preservation.test.js.map +1 -1
  15. package/dist/__tests__/optimize.test.js +0 -2
  16. package/dist/__tests__/optimize.test.js.map +1 -1
  17. package/dist/__tests__/repo-picker.test.js +1 -7
  18. package/dist/__tests__/repo-picker.test.js.map +1 -1
  19. package/dist/__tests__/review-comment-body.test.js +0 -35
  20. package/dist/__tests__/review-comment-body.test.js.map +1 -1
  21. package/dist/__tests__/review-models.test.js +3 -19
  22. package/dist/__tests__/review-models.test.js.map +1 -1
  23. package/dist/__tests__/runner.test.js +1 -29
  24. package/dist/__tests__/runner.test.js.map +1 -1
  25. package/dist/cli.js +0 -29
  26. package/dist/cli.js.map +1 -1
  27. package/dist/commands/onboard.d.ts +0 -11
  28. package/dist/commands/onboard.d.ts.map +1 -1
  29. package/dist/commands/onboard.js +2 -88
  30. package/dist/commands/onboard.js.map +1 -1
  31. package/dist/commands/review.d.ts.map +1 -1
  32. package/dist/commands/review.js +6 -65
  33. package/dist/commands/review.js.map +1 -1
  34. package/dist/commands/run.d.ts.map +1 -1
  35. package/dist/commands/run.js +3 -23
  36. package/dist/commands/run.js.map +1 -1
  37. package/dist/commands/status.d.ts.map +1 -1
  38. package/dist/commands/status.js +1 -53
  39. package/dist/commands/status.js.map +1 -1
  40. package/dist/commands/watch.d.ts.map +1 -1
  41. package/dist/commands/watch.js +6 -52
  42. package/dist/commands/watch.js.map +1 -1
  43. package/dist/config/loader.d.ts +1 -3
  44. package/dist/config/loader.d.ts.map +1 -1
  45. package/dist/config/loader.js +0 -13
  46. package/dist/config/loader.js.map +1 -1
  47. package/dist/config/review-model-tiers.json +3 -3
  48. package/dist/config/schema.d.ts +10 -254
  49. package/dist/config/schema.d.ts.map +1 -1
  50. package/dist/config/schema.js +2 -62
  51. package/dist/config/schema.js.map +1 -1
  52. package/dist/github/client.d.ts +1 -6
  53. package/dist/github/client.d.ts.map +1 -1
  54. package/dist/github/client.js +7 -15
  55. package/dist/github/client.js.map +1 -1
  56. package/dist/issues/ticket-ref.d.ts.map +1 -1
  57. package/dist/issues/ticket-ref.js +5 -6
  58. package/dist/issues/ticket-ref.js.map +1 -1
  59. package/dist/lib/clone.d.ts +0 -1
  60. package/dist/lib/clone.d.ts.map +1 -1
  61. package/dist/lib/clone.js +10 -32
  62. package/dist/lib/clone.js.map +1 -1
  63. package/dist/lib/comment-bodies.d.ts +0 -35
  64. package/dist/lib/comment-bodies.d.ts.map +1 -1
  65. package/dist/lib/comment-bodies.js +9 -43
  66. package/dist/lib/comment-bodies.js.map +1 -1
  67. package/dist/lib/repo-picker.d.ts +0 -3
  68. package/dist/lib/repo-picker.d.ts.map +1 -1
  69. package/dist/lib/repo-picker.js +9 -45
  70. package/dist/lib/repo-picker.js.map +1 -1
  71. package/dist/lib/review-models.d.ts +2 -15
  72. package/dist/lib/review-models.d.ts.map +1 -1
  73. package/dist/lib/review-models.js +6 -26
  74. package/dist/lib/review-models.js.map +1 -1
  75. package/dist/lib/runner.d.ts +0 -12
  76. package/dist/lib/runner.d.ts.map +1 -1
  77. package/dist/lib/runner.js +42 -186
  78. package/dist/lib/runner.js.map +1 -1
  79. package/dist/lib/workflow.d.ts +0 -9
  80. package/dist/lib/workflow.d.ts.map +1 -1
  81. package/dist/lib/workflow.js +0 -20
  82. package/dist/lib/workflow.js.map +1 -1
  83. package/dist/reviewers/claude.d.ts +1 -4
  84. package/dist/reviewers/claude.d.ts.map +1 -1
  85. package/dist/reviewers/claude.js +7 -39
  86. package/dist/reviewers/claude.js.map +1 -1
  87. package/dist/reviewers/codex.d.ts +1 -3
  88. package/dist/reviewers/codex.d.ts.map +1 -1
  89. package/dist/reviewers/codex.js +70 -76
  90. package/dist/reviewers/codex.js.map +1 -1
  91. package/dist/reviewers/conflict-resolve.d.ts +1 -3
  92. package/dist/reviewers/conflict-resolve.d.ts.map +1 -1
  93. package/dist/reviewers/conflict-resolve.js +6 -21
  94. package/dist/reviewers/conflict-resolve.js.map +1 -1
  95. package/dist/reviewers/fix.d.ts +2 -5
  96. package/dist/reviewers/fix.d.ts.map +1 -1
  97. package/dist/reviewers/fix.js +10 -26
  98. package/dist/reviewers/fix.js.map +1 -1
  99. package/get-started.md +4 -139
  100. package/package.json +3 -4
  101. package/assets/icon-256.png +0 -0
  102. package/assets/linear-comment.svg +0 -18
  103. package/assets/linear-onboard.svg +0 -30
  104. package/assets/linear-status.svg +0 -23
  105. package/assets/linear-test.svg +0 -34
  106. package/assets/skills/code-review/.crosscheck-skill.json +0 -9
  107. package/assets/skills/code-review/LICENSE +0 -21
  108. package/assets/skills/code-review/SKILL.md +0 -89
  109. package/assets/skills/code-review/agents/openai.yaml +0 -3
  110. package/assets/skills/code-review-skill/.crosscheck-skill.json +0 -9
  111. package/assets/skills/code-review-skill/LICENSE +0 -21
  112. package/assets/skills/code-review-skill/SKILL.md +0 -231
  113. package/assets/skills/code-review-skill/assets/pr-review-template.md +0 -137
  114. package/assets/skills/code-review-skill/assets/review-checklist.md +0 -123
  115. package/assets/skills/code-review-skill/reference/angular.md +0 -768
  116. package/assets/skills/code-review-skill/reference/architecture-review-guide.md +0 -472
  117. package/assets/skills/code-review-skill/reference/c.md +0 -890
  118. package/assets/skills/code-review-skill/reference/code-quality-universal.md +0 -488
  119. package/assets/skills/code-review-skill/reference/code-review-best-practices.md +0 -136
  120. package/assets/skills/code-review-skill/reference/common-bugs-checklist.md +0 -286
  121. package/assets/skills/code-review-skill/reference/cpp.md +0 -893
  122. package/assets/skills/code-review-skill/reference/cross-cutting/async-concurrency-patterns.md +0 -515
  123. package/assets/skills/code-review-skill/reference/cross-cutting/error-handling-principles.md +0 -492
  124. package/assets/skills/code-review-skill/reference/cross-cutting/n-plus-one-queries.md +0 -309
  125. package/assets/skills/code-review-skill/reference/cross-cutting/sql-injection-prevention.md +0 -308
  126. package/assets/skills/code-review-skill/reference/cross-cutting/xss-prevention.md +0 -264
  127. package/assets/skills/code-review-skill/reference/csharp.md +0 -525
  128. package/assets/skills/code-review-skill/reference/css-less-sass.md +0 -661
  129. package/assets/skills/code-review-skill/reference/django.md +0 -985
  130. package/assets/skills/code-review-skill/reference/fastapi.md +0 -580
  131. package/assets/skills/code-review-skill/reference/go.md +0 -993
  132. package/assets/skills/code-review-skill/reference/java.md +0 -409
  133. package/assets/skills/code-review-skill/reference/java8.md +0 -586
  134. package/assets/skills/code-review-skill/reference/kotlin.md +0 -1018
  135. package/assets/skills/code-review-skill/reference/nestjs.md +0 -593
  136. package/assets/skills/code-review-skill/reference/performance-review-guide.md +0 -816
  137. package/assets/skills/code-review-skill/reference/php.md +0 -684
  138. package/assets/skills/code-review-skill/reference/python.md +0 -1073
  139. package/assets/skills/code-review-skill/reference/qt.md +0 -757
  140. package/assets/skills/code-review-skill/reference/react.md +0 -871
  141. package/assets/skills/code-review-skill/reference/ruby.md +0 -964
  142. package/assets/skills/code-review-skill/reference/rust.md +0 -846
  143. package/assets/skills/code-review-skill/reference/security-review-guide.md +0 -494
  144. package/assets/skills/code-review-skill/reference/svelte.md +0 -1064
  145. package/assets/skills/code-review-skill/reference/swift.md +0 -936
  146. package/assets/skills/code-review-skill/reference/typescript.md +0 -1016
  147. package/assets/skills/code-review-skill/reference/vue.md +0 -924
  148. package/assets/skills/code-review-skill/reference/zig.md +0 -440
  149. package/assets/skills/code-review-skill/scripts/pr-analyzer.py +0 -435
  150. package/assets/skills/code-review-skill/scripts/test_pr_analyzer.py +0 -380
  151. package/assets/skills/codebase-design/.crosscheck-skill.json +0 -9
  152. package/assets/skills/codebase-design/DEEPENING.md +0 -37
  153. package/assets/skills/codebase-design/DESIGN-IT-TWICE.md +0 -44
  154. package/assets/skills/codebase-design/LICENSE +0 -21
  155. package/assets/skills/codebase-design/SKILL.md +0 -114
  156. package/assets/skills/codebase-design/agents/openai.yaml +0 -3
  157. package/assets/skills/diagnosing-bugs/.crosscheck-skill.json +0 -9
  158. package/assets/skills/diagnosing-bugs/LICENSE +0 -21
  159. package/assets/skills/diagnosing-bugs/SKILL.md +0 -134
  160. package/assets/skills/diagnosing-bugs/agents/openai.yaml +0 -3
  161. package/assets/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +0 -41
  162. package/dist/__tests__/can-write-verdict.test.d.ts +0 -2
  163. package/dist/__tests__/can-write-verdict.test.d.ts.map +0 -1
  164. package/dist/__tests__/can-write-verdict.test.js +0 -31
  165. package/dist/__tests__/can-write-verdict.test.js.map +0 -1
  166. package/dist/__tests__/linear-branding.test.d.ts +0 -2
  167. package/dist/__tests__/linear-branding.test.d.ts.map +0 -1
  168. package/dist/__tests__/linear-branding.test.js +0 -156
  169. package/dist/__tests__/linear-branding.test.js.map +0 -1
  170. package/dist/__tests__/linear-client.test.d.ts +0 -2
  171. package/dist/__tests__/linear-client.test.d.ts.map +0 -1
  172. package/dist/__tests__/linear-client.test.js +0 -120
  173. package/dist/__tests__/linear-client.test.js.map +0 -1
  174. package/dist/__tests__/linear-comment.test.d.ts +0 -2
  175. package/dist/__tests__/linear-comment.test.d.ts.map +0 -1
  176. package/dist/__tests__/linear-comment.test.js +0 -151
  177. package/dist/__tests__/linear-comment.test.js.map +0 -1
  178. package/dist/__tests__/linear-identity.test.d.ts +0 -2
  179. package/dist/__tests__/linear-identity.test.d.ts.map +0 -1
  180. package/dist/__tests__/linear-identity.test.js +0 -253
  181. package/dist/__tests__/linear-identity.test.js.map +0 -1
  182. package/dist/__tests__/linear-notify.test.d.ts +0 -2
  183. package/dist/__tests__/linear-notify.test.d.ts.map +0 -1
  184. package/dist/__tests__/linear-notify.test.js +0 -144
  185. package/dist/__tests__/linear-notify.test.js.map +0 -1
  186. package/dist/__tests__/linear-ref.test.d.ts +0 -2
  187. package/dist/__tests__/linear-ref.test.d.ts.map +0 -1
  188. package/dist/__tests__/linear-ref.test.js +0 -261
  189. package/dist/__tests__/linear-ref.test.js.map +0 -1
  190. package/dist/__tests__/linear-test-ref.test.d.ts +0 -2
  191. package/dist/__tests__/linear-test-ref.test.d.ts.map +0 -1
  192. package/dist/__tests__/linear-test-ref.test.js +0 -81
  193. package/dist/__tests__/linear-test-ref.test.js.map +0 -1
  194. package/dist/__tests__/linear-verify.test.d.ts +0 -2
  195. package/dist/__tests__/linear-verify.test.d.ts.map +0 -1
  196. package/dist/__tests__/linear-verify.test.js +0 -132
  197. package/dist/__tests__/linear-verify.test.js.map +0 -1
  198. package/dist/__tests__/linear-worker.test.d.ts +0 -2
  199. package/dist/__tests__/linear-worker.test.d.ts.map +0 -1
  200. package/dist/__tests__/linear-worker.test.js +0 -83
  201. package/dist/__tests__/linear-worker.test.js.map +0 -1
  202. package/dist/__tests__/linear-write-possible.test.d.ts +0 -2
  203. package/dist/__tests__/linear-write-possible.test.d.ts.map +0 -1
  204. package/dist/__tests__/linear-write-possible.test.js +0 -30
  205. package/dist/__tests__/linear-write-possible.test.js.map +0 -1
  206. package/dist/__tests__/repository-guidance.test.d.ts +0 -2
  207. package/dist/__tests__/repository-guidance.test.d.ts.map +0 -1
  208. package/dist/__tests__/repository-guidance.test.js +0 -107
  209. package/dist/__tests__/repository-guidance.test.js.map +0 -1
  210. package/dist/__tests__/review-strategy.test.d.ts +0 -2
  211. package/dist/__tests__/review-strategy.test.d.ts.map +0 -1
  212. package/dist/__tests__/review-strategy.test.js +0 -195
  213. package/dist/__tests__/review-strategy.test.js.map +0 -1
  214. package/dist/__tests__/skill-attribution.test.d.ts +0 -2
  215. package/dist/__tests__/skill-attribution.test.d.ts.map +0 -1
  216. package/dist/__tests__/skill-attribution.test.js +0 -53
  217. package/dist/__tests__/skill-attribution.test.js.map +0 -1
  218. package/dist/__tests__/skill-broker.test.d.ts +0 -2
  219. package/dist/__tests__/skill-broker.test.d.ts.map +0 -1
  220. package/dist/__tests__/skill-broker.test.js +0 -144
  221. package/dist/__tests__/skill-broker.test.js.map +0 -1
  222. package/dist/__tests__/skill-catalog.test.d.ts +0 -2
  223. package/dist/__tests__/skill-catalog.test.d.ts.map +0 -1
  224. package/dist/__tests__/skill-catalog.test.js +0 -40
  225. package/dist/__tests__/skill-catalog.test.js.map +0 -1
  226. package/dist/__tests__/skill-installer.test.d.ts +0 -2
  227. package/dist/__tests__/skill-installer.test.d.ts.map +0 -1
  228. package/dist/__tests__/skill-installer.test.js +0 -96
  229. package/dist/__tests__/skill-installer.test.js.map +0 -1
  230. package/dist/__tests__/skills-config.test.d.ts +0 -2
  231. package/dist/__tests__/skills-config.test.d.ts.map +0 -1
  232. package/dist/__tests__/skills-config.test.js +0 -12
  233. package/dist/__tests__/skills-config.test.js.map +0 -1
  234. package/dist/commands/linear-test.d.ts +0 -18
  235. package/dist/commands/linear-test.d.ts.map +0 -1
  236. package/dist/commands/linear-test.js +0 -130
  237. package/dist/commands/linear-test.js.map +0 -1
  238. package/dist/commands/skill.d.ts +0 -2
  239. package/dist/commands/skill.d.ts.map +0 -1
  240. package/dist/commands/skill.js +0 -16
  241. package/dist/commands/skill.js.map +0 -1
  242. package/dist/config/review-strategy.json +0 -202
  243. package/dist/lib/repository-guidance.d.ts +0 -2
  244. package/dist/lib/repository-guidance.d.ts.map +0 -1
  245. package/dist/lib/repository-guidance.js +0 -55
  246. package/dist/lib/repository-guidance.js.map +0 -1
  247. package/dist/lib/review-strategy.d.ts +0 -62
  248. package/dist/lib/review-strategy.d.ts.map +0 -1
  249. package/dist/lib/review-strategy.js +0 -235
  250. package/dist/lib/review-strategy.js.map +0 -1
  251. package/dist/linear/client.d.ts +0 -18
  252. package/dist/linear/client.d.ts.map +0 -1
  253. package/dist/linear/client.js +0 -67
  254. package/dist/linear/client.js.map +0 -1
  255. package/dist/linear/comment.d.ts +0 -20
  256. package/dist/linear/comment.d.ts.map +0 -1
  257. package/dist/linear/comment.js +0 -57
  258. package/dist/linear/comment.js.map +0 -1
  259. package/dist/linear/identity.d.ts +0 -59
  260. package/dist/linear/identity.d.ts.map +0 -1
  261. package/dist/linear/identity.js +0 -187
  262. package/dist/linear/identity.js.map +0 -1
  263. package/dist/linear/notify.d.ts +0 -35
  264. package/dist/linear/notify.d.ts.map +0 -1
  265. package/dist/linear/notify.js +0 -76
  266. package/dist/linear/notify.js.map +0 -1
  267. package/dist/linear/ref.d.ts +0 -13
  268. package/dist/linear/ref.d.ts.map +0 -1
  269. package/dist/linear/ref.js +0 -90
  270. package/dist/linear/ref.js.map +0 -1
  271. package/dist/linear/verify.d.ts +0 -26
  272. package/dist/linear/verify.d.ts.map +0 -1
  273. package/dist/linear/verify.js +0 -67
  274. package/dist/linear/verify.js.map +0 -1
  275. package/dist/skills/attribution.d.ts +0 -4
  276. package/dist/skills/attribution.d.ts.map +0 -1
  277. package/dist/skills/attribution.js +0 -14
  278. package/dist/skills/attribution.js.map +0 -1
  279. package/dist/skills/broker-server.d.ts +0 -2
  280. package/dist/skills/broker-server.d.ts.map +0 -1
  281. package/dist/skills/broker-server.js +0 -17
  282. package/dist/skills/broker-server.js.map +0 -1
  283. package/dist/skills/broker.d.ts +0 -42
  284. package/dist/skills/broker.d.ts.map +0 -1
  285. package/dist/skills/broker.js +0 -285
  286. package/dist/skills/broker.js.map +0 -1
  287. package/dist/skills/catalog.d.ts +0 -28
  288. package/dist/skills/catalog.d.ts.map +0 -1
  289. package/dist/skills/catalog.js +0 -104
  290. package/dist/skills/catalog.js.map +0 -1
  291. package/dist/skills/installer.d.ts +0 -10
  292. package/dist/skills/installer.d.ts.map +0 -1
  293. package/dist/skills/installer.js +0 -138
  294. package/dist/skills/installer.js.map +0 -1
  295. package/dist/skills/integrity.d.ts +0 -4
  296. package/dist/skills/integrity.d.ts.map +0 -1
  297. package/dist/skills/integrity.js +0 -36
  298. package/dist/skills/integrity.js.map +0 -1
  299. package/docs/dynamic-thoroughness.md +0 -286
  300. package/docs/linear-identity-contract.md +0 -139
  301. package/docs/linear-identity.md +0 -293
@@ -1 +0,0 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/skills/catalog.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb;AAMD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,eAAO,MAAM,uBAAuB,sEAAuE,CAAA;AAE3G,eAAO,MAAM,6BAA6B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAK1E,CAAA;AAOD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAE1G;AAGD,eAAO,MAAM,oBAAoB,QAA2C,CAAA;AAE5E,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAMxE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAElE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEnE;AAiDD,wBAAgB,iBAAiB,CAAC,OAAO,SAAqB,GAAG,aAAa,EAAE,CAE/E;AAED,wBAAgB,mBAAmB,CAAC,OAAO,SAAuB,GAAG,aAAa,EAAE,CAEnF;AAED,wBAAgB,gBAAgB,CAAC,aAAa,SAAuB,GAAG,aAAa,EAAE,CAMtF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC,GAAG,MAAM,CAErG"}
@@ -1,104 +0,0 @@
1
- import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
2
- import { join } from 'path';
3
- import { fileURLToPath } from 'url';
4
- import { homedir } from 'os';
5
- import yaml from 'js-yaml';
6
- import { computeSkillIntegrity } from './integrity.js';
7
- export const RECOMMENDED_SKILL_NAMES = ['code-review-skill', 'codebase-design', 'diagnosing-bugs'];
8
- export const BUNDLED_SKILL_RECOMMENDATIONS = {
9
- 'code-review': 'Best for evidence-rich repositories with documented standards and a clear issue or PRD; checks Standards and Spec separately.',
10
- 'code-review-skill': 'Best general default for broad language, correctness, security, performance, and architecture coverage.',
11
- 'codebase-design': 'Best for architecture-heavy changes: deep modules, small interfaces, clean seams, and testability.',
12
- 'diagnosing-bugs': 'Best for hard bugs and regressions: requires a reproducible signal, ranked hypotheses, and regression evidence.',
13
- };
14
- const COMPETING_SKILLS = {
15
- 'code-review': ['code-review-skill'],
16
- 'code-review-skill': ['code-review'],
17
- };
18
- export function findCompetingSkill(skillName, selectedNames) {
19
- return COMPETING_SKILLS[skillName]?.find(name => selectedNames.includes(name));
20
- }
21
- const BUNDLED_SKILLS_DIR = fileURLToPath(new URL('../../assets/skills/', import.meta.url));
22
- export const INSTALLED_SKILLS_DIR = join(homedir(), '.crosscheck', 'skills');
23
- export function readSkillFrontmatter(skillPath) {
24
- const content = readFileSync(join(skillPath, 'SKILL.md'), 'utf8');
25
- const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
26
- if (!match)
27
- return {};
28
- const parsed = yaml.load(match[1]);
29
- return typeof parsed === 'object' && parsed !== null ? parsed : {};
30
- }
31
- export function isValidSkillAuthor(value) {
32
- return typeof value === 'string' && /^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,37}[a-zA-Z0-9])?$/.test(value);
33
- }
34
- export function isValidSkillLicense(value) {
35
- return typeof value === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9.+() -]{0,99}$/.test(value);
36
- }
37
- function isSkillReceipt(value) {
38
- if (typeof value !== 'object' || value === null)
39
- return false;
40
- const receipt = value;
41
- return receipt.schemaVersion === 1
42
- && typeof receipt.name === 'string' && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(receipt.name)
43
- && isValidSkillAuthor(receipt.author)
44
- && isValidSkillLicense(receipt.license)
45
- && typeof receipt.source === 'string' && receipt.source.length > 0
46
- && typeof receipt.revision === 'string' && receipt.revision.length > 0
47
- && typeof receipt.integrity === 'string' && /^sha256:[0-9a-f]{64}$/.test(receipt.integrity);
48
- }
49
- function loadSkills(rootDir, skipInvalid) {
50
- if (!existsSync(rootDir))
51
- return [];
52
- return readdirSync(rootDir, { withFileTypes: true })
53
- .filter(entry => entry.isDirectory() && !entry.name.startsWith('.'))
54
- .flatMap(entry => {
55
- const path = join(rootDir, entry.name);
56
- try {
57
- const receiptPath = join(path, '.crosscheck-skill.json');
58
- if (statSync(receiptPath).size > 64 * 1024)
59
- throw new Error(`Invalid skill receipt: ${entry.name}`);
60
- const integrity = computeSkillIntegrity(path);
61
- const receipt = JSON.parse(readFileSync(receiptPath, 'utf8'));
62
- const frontmatter = readSkillFrontmatter(path);
63
- if (!isSkillReceipt(receipt) || receipt.name !== entry.name || frontmatter.name !== receipt.name
64
- || typeof frontmatter.description !== 'string' || !frontmatter.description.trim()
65
- || integrity !== receipt.integrity) {
66
- throw new Error(`Invalid skill package: ${entry.name}`);
67
- }
68
- return [{
69
- name: receipt.name,
70
- description: frontmatter.description.trim(),
71
- author: receipt.author,
72
- license: receipt.license,
73
- source: receipt.source,
74
- revision: receipt.revision,
75
- integrity: receipt.integrity,
76
- path,
77
- }];
78
- }
79
- catch (err) {
80
- if (skipInvalid)
81
- return [];
82
- throw err;
83
- }
84
- })
85
- .sort((a, b) => a.name.localeCompare(b.name));
86
- }
87
- export function loadBundledSkills(rootDir = BUNDLED_SKILLS_DIR) {
88
- return loadSkills(rootDir, false);
89
- }
90
- export function loadInstalledSkills(rootDir = INSTALLED_SKILLS_DIR) {
91
- return loadSkills(rootDir, true);
92
- }
93
- export function loadSkillCatalog(installedRoot = INSTALLED_SKILLS_DIR) {
94
- const skills = new Map(loadBundledSkills().map(skill => [skill.name, skill]));
95
- for (const skill of loadInstalledSkills(installedRoot)) {
96
- if (!skills.has(skill.name))
97
- skills.set(skill.name, skill);
98
- }
99
- return [...skills.values()].sort((a, b) => a.name.localeCompare(b.name));
100
- }
101
- export function formatSkillIdentity(skill) {
102
- return `${skill.name} (by @${skill.author}, ${skill.license})`;
103
- }
104
- //# sourceMappingURL=catalog.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/skills/catalog.ts"],"names":[],"mappings":"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"}
@@ -1,10 +0,0 @@
1
- import { type SkillIdentity } from './catalog.js';
2
- export { computeSkillIntegrity } from './integrity.js';
3
- export interface InstallSkillOptions {
4
- installDir?: string;
5
- }
6
- export declare class SkillInstallError extends Error {
7
- }
8
- export declare function redactSkillSource(source: string): string;
9
- export declare function installSkill(source: string, opts?: InstallSkillOptions): Promise<SkillIdentity>;
10
- //# sourceMappingURL=installer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../src/skills/installer.ts"],"names":[],"mappings":"AAIA,OAAO,EAA+H,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAG9K,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAEtD,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAE/C,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAYxD;AAuCD,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,aAAa,CAAC,CAgEzG"}
@@ -1,138 +0,0 @@
1
- import { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'fs';
2
- import { basename, join, resolve } from 'path';
3
- import { tmpdir } from 'os';
4
- import { execa } from 'execa';
5
- import { INSTALLED_SKILLS_DIR, isValidSkillAuthor, isValidSkillLicense, loadBundledSkills, loadInstalledSkills, readSkillFrontmatter } from './catalog.js';
6
- import { computeSkillIntegrity } from './integrity.js';
7
- export { computeSkillIntegrity } from './integrity.js';
8
- export class SkillInstallError extends Error {
9
- }
10
- export function redactSkillSource(source) {
11
- if (existsSync(source))
12
- return resolve(source);
13
- try {
14
- const url = new URL(source);
15
- url.username = '';
16
- url.password = '';
17
- url.search = '';
18
- url.hash = '';
19
- return url.toString();
20
- }
21
- catch {
22
- return source;
23
- }
24
- }
25
- function detectLicense(rootDir, declared) {
26
- if (typeof declared === 'string' && declared.trim())
27
- return declared.trim();
28
- const licenseName = ['LICENSE', 'LICENSE.md', 'COPYING'].find(name => existsSync(join(rootDir, name)));
29
- if (!licenseName)
30
- return 'UNKNOWN';
31
- const license = readFileSync(join(rootDir, licenseName), 'utf8');
32
- if (/MIT License/i.test(license))
33
- return 'MIT';
34
- if (/Apache License[\s\S]*Version 2\.0/i.test(license))
35
- return 'Apache-2.0';
36
- if (/BSD 3-Clause/i.test(license))
37
- return 'BSD-3-Clause';
38
- if (/ISC License/i.test(license))
39
- return 'ISC';
40
- return 'UNKNOWN';
41
- }
42
- function detectAuthor(source, declared) {
43
- if (typeof declared === 'string' && declared.trim())
44
- return declared.trim().replace(/^@/, '');
45
- return /github\.com[/:]([^/]+)/i.exec(source)?.[1] ?? 'local';
46
- }
47
- async function checkoutSource(source) {
48
- if (existsSync(source)) {
49
- const path = resolve(source);
50
- if (lstatSync(path).isSymbolicLink())
51
- throw new SkillInstallError('Skill source cannot be a symbolic link');
52
- if (!statSync(path).isDirectory())
53
- throw new SkillInstallError(`Skill source is not a directory: ${source}`);
54
- return { path, revision: 'local' };
55
- }
56
- const cleanup = mkdtempSync(join(tmpdir(), 'crosscheck-skill-clone-'));
57
- const path = join(cleanup, 'source');
58
- try {
59
- await execa('git', ['clone', '--depth', '1', source, path]);
60
- const revision = (await execa('git', ['-C', path, 'rev-parse', 'HEAD'])).stdout.trim();
61
- return { path, revision, cleanup };
62
- }
63
- catch (err) {
64
- rmSync(cleanup, { recursive: true, force: true });
65
- throw new SkillInstallError(`Could not clone skill source: ${err instanceof Error ? err.message : String(err)}`);
66
- }
67
- }
68
- export async function installSkill(source, opts = {}) {
69
- const installDir = resolve(opts.installDir ?? INSTALLED_SKILLS_DIR);
70
- const checkout = await checkoutSource(source);
71
- try {
72
- if (!existsSync(join(checkout.path, 'SKILL.md'))) {
73
- throw new SkillInstallError('Skill package must contain SKILL.md');
74
- }
75
- try {
76
- computeSkillIntegrity(checkout.path);
77
- }
78
- catch (err) {
79
- throw new SkillInstallError(err instanceof Error ? err.message : String(err));
80
- }
81
- let frontmatter;
82
- try {
83
- frontmatter = readSkillFrontmatter(checkout.path);
84
- }
85
- catch (err) {
86
- throw new SkillInstallError(`Invalid SKILL.md frontmatter: ${err instanceof Error ? err.message : String(err)}`);
87
- }
88
- const name = typeof frontmatter.name === 'string' ? frontmatter.name : '';
89
- if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) {
90
- throw new SkillInstallError(`Invalid skill name: ${name || '(missing)'}`);
91
- }
92
- if (typeof frontmatter.description !== 'string' || !frontmatter.description.trim()) {
93
- throw new SkillInstallError('Skill frontmatter must include a description');
94
- }
95
- if (loadBundledSkills().some(skill => skill.name === name)) {
96
- throw new SkillInstallError(`Skill ${name} is already bundled with Crosscheck`);
97
- }
98
- mkdirSync(installDir, { recursive: true });
99
- const target = join(installDir, name);
100
- if (existsSync(target)) {
101
- throw new SkillInstallError(`Skill ${name} is already installed`);
102
- }
103
- const pendingTarget = join(installDir, `.install-${name}-${process.pid}`);
104
- try {
105
- cpSync(checkout.path, pendingTarget, {
106
- recursive: true,
107
- filter: path => basename(path) !== '.git' && basename(path) !== '.crosscheck-skill.json',
108
- });
109
- const author = detectAuthor(source, frontmatter.author);
110
- const license = detectLicense(checkout.path, frontmatter.license);
111
- if (!isValidSkillAuthor(author))
112
- throw new SkillInstallError(`Invalid skill author: ${author}`);
113
- if (!isValidSkillLicense(license))
114
- throw new SkillInstallError(`Invalid skill license: ${license}`);
115
- const receipt = {
116
- schemaVersion: 1,
117
- name,
118
- author,
119
- license,
120
- source: redactSkillSource(source),
121
- revision: checkout.revision,
122
- integrity: computeSkillIntegrity(pendingTarget),
123
- };
124
- writeFileSync(join(pendingTarget, '.crosscheck-skill.json'), `${JSON.stringify(receipt, null, 2)}\n`);
125
- renameSync(pendingTarget, target);
126
- return loadInstalledSkills(installDir).find(skill => skill.name === name);
127
- }
128
- catch (err) {
129
- rmSync(pendingTarget, { recursive: true, force: true });
130
- throw err;
131
- }
132
- }
133
- finally {
134
- if (checkout.cleanup)
135
- rmSync(checkout.cleanup, { recursive: true, force: true });
136
- }
137
- }
138
- //# sourceMappingURL=installer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"installer.js","sourceRoot":"","sources":["../../src/skills/installer.ts"],"names":[],"mappings":"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"}
@@ -1,4 +0,0 @@
1
- export declare const MAX_SKILL_PACKAGE_FILES = 1000;
2
- export declare const MAX_SKILL_PACKAGE_BYTES: number;
3
- export declare function computeSkillIntegrity(rootDir: string): string;
4
- //# sourceMappingURL=integrity.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"integrity.d.ts","sourceRoot":"","sources":["../../src/skills/integrity.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,uBAAuB,OAAQ,CAAA;AAC5C,eAAO,MAAM,uBAAuB,QAAmB,CAAA;AAevD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAc7D"}
@@ -1,36 +0,0 @@
1
- import { createHash } from 'crypto';
2
- import { lstatSync, readFileSync, readdirSync, statSync } from 'fs';
3
- import { join, relative, sep } from 'path';
4
- export const MAX_SKILL_PACKAGE_FILES = 1_000;
5
- export const MAX_SKILL_PACKAGE_BYTES = 10 * 1024 * 1024;
6
- function packageFiles(rootDir, currentDir, files) {
7
- for (const entry of readdirSync(currentDir, { withFileTypes: true })) {
8
- if (entry.name === '.git' || entry.name === '.crosscheck-skill.json')
9
- continue;
10
- const path = join(currentDir, entry.name);
11
- if (lstatSync(path).isSymbolicLink())
12
- throw new Error('Skill packages cannot contain symbolic links');
13
- if (entry.isDirectory())
14
- packageFiles(rootDir, path, files);
15
- else if (entry.isFile()) {
16
- files.push(path);
17
- if (files.length > MAX_SKILL_PACKAGE_FILES)
18
- throw new Error(`Skill packages cannot contain more than ${MAX_SKILL_PACKAGE_FILES} files`);
19
- }
20
- }
21
- }
22
- export function computeSkillIntegrity(rootDir) {
23
- const files = [];
24
- packageFiles(rootDir, rootDir, files);
25
- files.sort((a, b) => relative(rootDir, a).localeCompare(relative(rootDir, b)));
26
- const totalBytes = files.reduce((sum, path) => sum + statSync(path).size, 0);
27
- if (totalBytes > MAX_SKILL_PACKAGE_BYTES)
28
- throw new Error('Skill packages cannot exceed 10 MiB');
29
- const hash = createHash('sha256');
30
- for (const path of files) {
31
- hash.update(relative(rootDir, path).split(sep).join('/')).update('\0');
32
- hash.update(readFileSync(path)).update('\0');
33
- }
34
- return `sha256:${hash.digest('hex')}`;
35
- }
36
- //# sourceMappingURL=integrity.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"integrity.js","sourceRoot":"","sources":["../../src/skills/integrity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;AACnE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAE1C,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,CAAA;AAC5C,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAA;AAEvD,SAAS,YAAY,CAAC,OAAe,EAAE,UAAkB,EAAE,KAAe;IACxE,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACrE,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB;YAAE,SAAQ;QAC9E,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACzC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAA;QACrG,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;aACtD,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAChB,IAAI,KAAK,CAAC,MAAM,GAAG,uBAAuB;gBAAE,MAAM,IAAI,KAAK,CAAC,2CAA2C,uBAAuB,QAAQ,CAAC,CAAA;QACzI,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,OAAe;IACnD,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;IACrC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IAE9E,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;IAC5E,IAAI,UAAU,GAAG,uBAAuB;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;IAEhG,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAA;IACjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACtE,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC9C,CAAC;IACD,OAAO,UAAU,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAA;AACvC,CAAC"}
@@ -1,286 +0,0 @@
1
- # Dynamic thoroughness
2
-
3
- **Strategy version 1.0.0 · updated 2026-08-07 · re-verify every 60 days.**
4
-
5
- This document is the analysis behind [`src/config/review-strategy.json`](../src/config/review-strategy.json).
6
- The JSON is the policy crosscheck executes; this file is why the policy says what it
7
- says. They are versioned together: a change to one without the other is drift, and
8
- `npm run verify:strategy` is the check that makes that drift loud.
9
-
10
- ---
11
-
12
- ## 1. The problem
13
-
14
- One PR is not like another. A lockfile bump and a rewrite of the session handler
15
- are both "a PR", and crosscheck used to review them identically — one configured
16
- `quality.tier`, one model, one effort level, the same review→fix→recheck loop.
17
- That is wrong in both directions at once: it overpays on the lockfile and
18
- under-reviews the session handler.
19
-
20
- The failure that prompted this was quieter than either. `review-model-tiers.json`
21
- pinned `claude-opus-4-8` for the `thorough` tier and kept pinning it after
22
- `claude-opus-5` shipped at identical $5/$25 pricing with a higher coding score.
23
- Nothing broke. Nothing warned. Every `thorough` review just got worse than it had
24
- to be, for months. A model pin is a perishable asset, and nothing in the repo
25
- treated it as one.
26
-
27
- So the policy has two jobs: choose per PR, and tell a human when the inputs it
28
- chose from have moved.
29
-
30
- ---
31
-
32
- ## 2. Sources, and how to re-verify
33
-
34
- `sources[]` in the JSON lists the pages the numbers came from, each with the
35
- strings that must still appear on it. `scripts/verify-review-strategy.mjs`
36
- fetches them and compares.
37
-
38
- | Source | What it fixes |
39
- |---|---|
40
- | [Anthropic model catalog](https://platform.claude.com/docs/en/about-claude/models/overview) | Claude model IDs and availability |
41
- | [Anthropic effort parameter](https://platform.claude.com/docs/en/build-with-claude/effort) | The effort ladder, including `xhigh` |
42
- | [OpenAI model catalog](https://developers.openai.com/api/docs/models) | Codex model IDs |
43
-
44
- Benchmarks are recorded in `benchmarks` with their own `as_of` dates and caveats,
45
- because they age faster than prices:
46
-
47
- - **SWE-bench Verified** — resolving real GitHub issues in Python repos. A
48
- backend/general proxy only. Comparable within one leaderboard run; cross-run
49
- positions are indicative.
50
- - **Frontend Code Arena** — blind pairwise human preference on generated frontend
51
- output. Measures *generation*, not defect detection. Claude Opus 5 is absent
52
- from the retrieved leaderboard: a missing measurement, not a low score.
53
-
54
- Re-verification is not automatic and is not meant to be. Model choice is a
55
- judgement call backed by benchmarks that need reading. The weekly
56
- `Review Strategy` workflow fetches the sources and opens an issue when a check
57
- string disappears; a human then re-reads and edits. The job's job is to say *the
58
- inputs moved*, never to move the policy itself.
59
-
60
- Network failures are deliberately not drift. A fetch that never landed says
61
- nothing about the policy, so the verifier treats unreachable sources as warnings
62
- on `pull_request` and as errors only on `schedule`, where someone is looking.
63
-
64
- ---
65
-
66
- ## 3. The model catalog
67
-
68
- Prices are USD per million tokens.
69
-
70
- | Model | Vendor | In | Out | SWE-bench V | Frontend Elo | Effort levels |
71
- |---|---|---:|---:|---:|---:|---|
72
- | `claude-opus-5` | claude | 5 | 25 | 0.960 | — | low…max |
73
- | `claude-sonnet-5` | claude | 3 | 15 | 0.852 | — | low…max |
74
- | `claude-haiku-4-5-20251001` | claude | 1 | 5 | 0.733 | — | *(none)* |
75
- | `gpt-5.6-sol` | codex | 5 | 30 | — | 1618 | none…max |
76
- | `gpt-5.6-terra` | codex | 2 | 12 | — | — | none…max |
77
- | `gpt-5.6-luna` | codex | 0.2 | 1.2 | — | — | none…max |
78
- | `kimi-k3` | opencode *(planned)* | 3 | 15 | — | 1679 | *(none)* |
79
- | `glm-5.2` | opencode *(planned)* | 1.4 | 4.4 | — | 1587 | *(none)* |
80
- | `deepseek-v4-pro` | opencode *(planned)* | 0.435 | 0.87 | 0.806 | — | none, high, max |
81
- | `deepseek-v4-flash` | opencode *(planned)* | 0.14 | 0.28 | 0.790 | — | none, high, max |
82
-
83
- `banned_models` is a separate list from "not currently routed", because the
84
- reason matters. `claude-fable-5` is banned on cost: $50/MTok output is 2× Opus 5
85
- for a *lower* SWE-bench Verified score (95.0 vs 96.0). At the measured ~48k
86
- output tokens per review that is $2.40 a call with no capability justification.
87
- The verifier fails if any vendor tier or domain preference ever routes to it.
88
-
89
- ---
90
-
91
- ## 4. Tiers and domains
92
-
93
- Tiers are the vendor-facing ladder:
94
-
95
- | Tier | Claude | Codex | OpenCode *(planned)* |
96
- |---|---|---|---|
97
- | `fast` | Haiku 4.5 | GPT-5.6 Luna | deepseek-v4-flash |
98
- | `balanced` | Sonnet 5 | GPT-5.6 Terra | glm-5.2 |
99
- | `thorough` | Opus 5 | GPT-5.6 Sol | kimi-k3 |
100
-
101
- Capability, though, is domain-shaped rather than scalar. Opus 5 leads backend
102
- defect resolution; kimi-k3 leads the frontend arena. A single ladder cannot
103
- express that, so `domains` carries a preference list per tier. The domain is
104
- detected from changed-file extensions; `mixed` and `unknown` both fall back to
105
- `backend`, the conservative default — the backend list is `measured`, the
106
- frontend list is still a `hypothesis` pending a local A/B.
107
-
108
- ---
109
-
110
- ## 5. PR classes
111
-
112
- **Order is the routing logic — first match wins.** The list is in
113
- `pr_classes`, and `risky` sits second, immediately after `generated`, so
114
- consequence-based promotion dominates every cheapening rule below it. A
115
- deletion-only PR that removes auth code, or a two-file change to a migration,
116
- must not fall through to `fast`.
117
-
118
- | # | Class | Tier · effort | Steps |
119
- |---|---|---|---|
120
- | 1 | `generated` — every file generated or vendored | *skip* | — |
121
- | 2 | `risky` — security / data-critical | thorough · high | review, fix, recheck |
122
- | 3 | `deletion_only` | fast · medium | review |
123
- | 4 | `docs` | balanced · high | review |
124
- | 5 | `test_only` | fast · medium | review, fix |
125
- | 6 | `config_infra` | balanced · high | review, fix, recheck |
126
- | 7 | `trivial` | fast · medium | review, fix |
127
- | 8 | `standard` — fallthrough | balanced · medium | review, fix, recheck |
128
-
129
- Two classes drop the fix loop on purpose. `docs` is open-ended design
130
- negotiation that no automated fix loop terminates. `deletion_only` carries
131
- reference-breakage risk rather than defect density — a wrong deletion is
132
- reverted, not patched — so it gets a review focused on dangling imports, call
133
- sites, config keys, route registrations and stale doc pointers.
134
-
135
- The last entry must have an empty `match`: it is the fallthrough, and
136
- `resolveReviewStrategy` falls back to it when nothing matches. The verifier
137
- asserts this.
138
-
139
- ### Path patterns are anchored
140
-
141
- Class patterns match path *segments and extensions*, not substrings. An
142
- unanchored list is worse than no list:
143
-
144
- - `auth` matched `author.ts`, `authors.ts` and `oauth.ts`;
145
- - `migration` matched this repo's own `MIGRATION_AND_GROWTH.md`, promoting a
146
- pure-docs PR to `thorough` with a full fix/recheck loop;
147
- - `spec` matched `src/lib/pr-spec.ts` and `test` matched any `latest.ts`,
148
- routing ordinary source changes to `test_only` with a test-focused prompt.
149
-
150
- Over-promotion is safe but expensive; under-review is neither. Both are bugs.
151
-
152
- ### Size caps fail closed
153
-
154
- `src_churn_max` can only be evaluated when churn is attributable — the PR
155
- context carries whole-PR `additions`/`deletions`, not per-file counts, so the
156
- total is only meaningful when every changed file is source. When it is not, the
157
- cap fails **closed** (the churn reads as `Infinity` and the class does not
158
- match). Failing open cost us a real misroute: `['package.json',
159
- 'src/lib/runner.ts', 'src/lib/board.ts']` at +3000/−2000 satisfied
160
- `files_max: 3`, the unattributable churn read as `0`, and a 5,000-line change to
161
- the core runner was routed to `fast` with no recheck.
162
-
163
- ---
164
-
165
- ## 6. Design decisions
166
-
167
- ### 6.1 Why a versioned policy file, not code
168
-
169
- Every crosscheck comment cites `strategy=<version>`. A review that ran three
170
- months ago has to stay explicable by the policy in force when it ran, and that is
171
- only possible if the policy is a versioned artifact rather than a diff of
172
- `if` statements. It also puts the routing decision and the sentence explaining it
173
- in the same record, so the two cannot drift apart: `reason` is quoted verbatim
174
- into the PR comment, which is why the verifier fails any class missing one.
175
-
176
- `review-model-tiers.json` is retained for backwards compatibility and is
177
- superseded by this file.
178
-
179
- ### 6.2 Why capability is not a single number
180
-
181
- See §4. A tier ladder is a cost ladder that happens to correlate with capability
182
- inside one vendor. Across vendors and across domains it stops correlating, which
183
- is why `domains.*.preferred` exists and why each entry carries a `confidence`
184
- field. `measured` and `hypothesis` are load-bearing words here — the frontend
185
- list is a hypothesis and is labelled as one.
186
-
187
- ### 6.3 Why `thorough` moved to Opus 5
188
-
189
- Identical $5/$25 pricing, higher SWE-bench Verified (0.960 vs the 4.8 pin it
190
- replaced). There is no trade here; the old pin was a silent capability loss, and
191
- it persisted only because nothing was watching. §2 exists so the next one does
192
- not persist.
193
-
194
- ### 6.4 Models with no effort ladder — the OpenCode case
195
-
196
- Most open-weight models expose no effort parameter at all: `kimi-k3`, `glm-5.2`
197
- and `claude-haiku-4-5-20251001` all have `effort_levels: []`, and the deepseek
198
- models expose only `none`/`high`/`max`. An escalation step defined purely as
199
- "raise effort" silently no-ops on every one of them.
200
-
201
- So the ladder declares `effort_fallback: "promote_tier"`: where effort cannot
202
- rise, the escalation degrades to a model step instead. `escalate()` implements
203
- this by clamping the round's target effort to what the model actually accepts and
204
- promoting a tier only when the clamped value is not an increase over the round
205
- before — otherwise a model whose ladder tops out below `xhigh` comes out of
206
- round 3 *weaker* than it left round 2.
207
-
208
- The verifier enforces the invariant directly: if any model has no effort levels,
209
- `ladder.effort_fallback` must be set.
210
-
211
- ### 6.5 Escalate on measured non-convergence, never on predicted complexity
212
-
213
- The ladder is bounded — 3 rounds, 5 blocking findings, 60 wall-clock minutes —
214
- and each round narrows scope rather than widening it. Round 2 rechecks the delta
215
- plus the open findings, not the whole PR again. The model never weakens across
216
- rounds; only its scope shrinks. Round 4 is a handoff to a human with a digest,
217
- not a fourth attempt.
218
-
219
- ### 6.6 Cross-vendor vs single-vendor
220
-
221
- Cross-vendor buys an independent second perspective: in the census a vendor
222
- switch broke a stalled loop twice (PR #2057, #2060) after same-vendor escalation
223
- had failed for three to six rounds. Its round-3 lever is *switch vendor and raise
224
- effort*, and it caps at 3 rounds. Single-vendor buys one CLI to authenticate,
225
- version and monitor, with no routing logic; its round-3 lever is *raise effort,
226
- then promote one tier*, because no fresh-eyes option exists, and it caps at 2.
227
-
228
- ### 6.7 Conflict resolution is not a review
229
-
230
- `conflict_resolve` runs at `fast`/`low` regardless of class. Resolving conflict
231
- markers is mechanical text surgery bounded by the markers themselves — measured
232
- at 37s against ~643s for a review. Spending review-grade capability on it buys
233
- nothing.
234
-
235
- ### 6.8 What classification may and may not conclude
236
-
237
- Two rules constrain the classifier, and they are the reason it stays a small
238
- static function instead of growing into a difficulty predictor:
239
-
240
- 1. **It may set a floor, or promote on consequence.** A security path is
241
- reviewed thoroughly because a miss there is expensive. That is a statement
242
- about cost of failure, which the file list genuinely supports.
243
- 2. **It may not predict that a PR will be hard.** Static features — file count,
244
- line count, extension mix — do not support that inference. Difficulty is
245
- discovered by reviewing, not before it. So there is no "this looks tricky,
246
- use the big model" rule, and there never should be.
247
-
248
- Escalation is how difficulty gets handled, and it is driven by *measured*
249
- non-convergence: the review came back BLOCK, the fix did not resolve it, so the
250
- next round escalates. That is evidence. A file-count heuristic is not.
251
-
252
- ---
253
-
254
- ## 7. Runtime status
255
-
256
- `runtime_status: "defined_not_wired"`. This file and the JSON are the adopted
257
- policy, but no runtime code reads `resolveReviewStrategy()` yet —
258
- `resolveClaudeModel` / `resolveCodexModel` still apply the single configured
259
- `quality.tier`. Verification, documentation and the citation contract are in
260
- place ahead of the resolver so the policy is settled before it is enforced.
261
-
262
- Consequently:
263
-
264
- - `quality.mode` stays **optional**. Unset means `fixed` — the legacy behaviour —
265
- and it will stay that way until the resolver lands, so nothing silently changes
266
- tier on upgrade.
267
- - `citation.annotation_fields` (`strategy`, `class`, `tier`) is a declared
268
- contract, not yet emitted by `src/lib/annotation.ts`.
269
-
270
- Wiring is the next change, and it is a separate one. It needs the PR's changed-file
271
- list threaded to the reviewers, the `strategy` argument passed at each
272
- `resolve*Model` call site, and the annotation fields emitted — none of which
273
- belongs in the PR that settles the policy.
274
-
275
- ---
276
-
277
- ## 8. Cadence
278
-
279
- `review_interval_days: 60`. The verifier warns past 45 days and fails past 60.
280
- The `Review Strategy` workflow runs Mondays at 09:15 UTC — fifteen minutes after
281
- `model-tier-sources`, so the two do not race the same provider docs — and on any
282
- PR touching the strategy, the verifier or this document.
283
-
284
- To refresh: re-read the `sources` and `benchmarks` pages, update prices, scores
285
- and tier assignments in `src/config/review-strategy.json`, refresh the analysis
286
- here, bump `version` and `updated`, then run `npm run verify:strategy`.