@iceinvein/agent-skills 0.21.1 → 0.21.4

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 (297) hide show
  1. package/package.json +3 -2
  2. package/skills/index.json +4 -4
  3. package/skills/magpie/README.md +3 -3
  4. package/skills/magpie/SKILL.md +24 -11
  5. package/skills/magpie/bin/magpie.ts +84 -6
  6. package/skills/magpie/evals/README.md +15 -0
  7. package/skills/magpie/evals/codex-missing-falls-back/fixture.sh +63 -22
  8. package/skills/magpie/evals/post-folds-selection-events/fixture.sh +8 -2
  9. package/skills/magpie/evals/quality/README.md +141 -0
  10. package/skills/magpie/evals/quality/__tests__/replay-critic.test.ts +31 -0
  11. package/skills/magpie/evals/quality/__tests__/score.test.ts +325 -0
  12. package/skills/magpie/evals/quality/baseline.ts +80 -0
  13. package/skills/magpie/evals/quality/corpus.ts +259 -0
  14. package/skills/magpie/evals/quality/replay-critic.ts +277 -0
  15. package/skills/magpie/evals/quality/replay-full.ts +188 -0
  16. package/skills/magpie/evals/quality/score.ts +251 -0
  17. package/skills/magpie/evals/report-ends-the-turn/fixture.sh +5 -1
  18. package/skills/magpie/references/critic.md +178 -40
  19. package/skills/magpie/references/peer-review.md +5 -4
  20. package/skills/magpie/references/scout.md +18 -3
  21. package/skills/magpie/references/specialists.md +36 -22
  22. package/skills/magpie/scripts/__tests__/cleanup-cmd.test.ts +28 -0
  23. package/skills/magpie/scripts/__tests__/critic-cmd.test.ts +260 -0
  24. package/skills/magpie/scripts/__tests__/critic.test.ts +328 -0
  25. package/skills/magpie/scripts/__tests__/dedupe-cmd.test.ts +89 -0
  26. package/skills/magpie/scripts/__tests__/dedupe.test.ts +43 -1
  27. package/skills/magpie/scripts/__tests__/evidence-filter.test.ts +155 -2
  28. package/skills/magpie/scripts/__tests__/helper.test.ts +80 -0
  29. package/skills/magpie/scripts/__tests__/labels.test.ts +152 -0
  30. package/skills/magpie/scripts/__tests__/open-cmd.test.ts +28 -0
  31. package/skills/magpie/scripts/__tests__/pipeline-e2e.test.ts +1 -0
  32. package/skills/magpie/scripts/__tests__/post-cmd.test.ts +47 -0
  33. package/skills/magpie/scripts/__tests__/refresh.test.ts +23 -0
  34. package/skills/magpie/scripts/__tests__/render-action-bar.test.ts +63 -5
  35. package/skills/magpie/scripts/__tests__/render-annotation.test.ts +38 -0
  36. package/skills/magpie/scripts/__tests__/render-cmd.test.ts +48 -0
  37. package/skills/magpie/scripts/__tests__/render-diff.test.ts +34 -0
  38. package/skills/magpie/scripts/__tests__/render-findings.test.ts +111 -7
  39. package/skills/magpie/scripts/__tests__/render-issues-list.test.ts +186 -1
  40. package/skills/magpie/scripts/__tests__/serve-cmd.test.ts +19 -0
  41. package/skills/magpie/scripts/__tests__/server.test.ts +76 -0
  42. package/skills/magpie/scripts/__tests__/skill-lint.test.ts +148 -9
  43. package/skills/magpie/scripts/__tests__/tests-check.test.ts +57 -0
  44. package/skills/magpie/scripts/__tests__/types.test.ts +76 -29
  45. package/skills/magpie/scripts/cleanup-cmd.ts +6 -0
  46. package/skills/magpie/scripts/critic-cmd.ts +183 -0
  47. package/skills/magpie/scripts/critic.ts +273 -0
  48. package/skills/magpie/scripts/dedupe-cmd.ts +14 -4
  49. package/skills/magpie/scripts/dedupe.ts +41 -0
  50. package/skills/magpie/scripts/evidence-filter.ts +81 -17
  51. package/skills/magpie/scripts/helper.js +87 -9
  52. package/skills/magpie/scripts/labels-cmd.ts +43 -0
  53. package/skills/magpie/scripts/labels.ts +99 -0
  54. package/skills/magpie/scripts/open-cmd.ts +10 -3
  55. package/skills/magpie/scripts/post-cmd.ts +26 -2
  56. package/skills/magpie/scripts/preview-cmd.ts +4 -0
  57. package/skills/magpie/scripts/refresh.ts +21 -17
  58. package/skills/magpie/scripts/render-action-bar.ts +21 -4
  59. package/skills/magpie/scripts/render-annotation.ts +36 -2
  60. package/skills/magpie/scripts/render-cmd.ts +21 -2
  61. package/skills/magpie/scripts/render-diff.ts +6 -2
  62. package/skills/magpie/scripts/render-findings.ts +21 -3
  63. package/skills/magpie/scripts/render-issues-list.ts +66 -16
  64. package/skills/magpie/scripts/server.ts +8 -1
  65. package/skills/magpie/scripts/tests-check.ts +23 -2
  66. package/skills/magpie/scripts/types.ts +37 -31
  67. package/skills/magpie/skill.json +2 -2
  68. package/skills/magpie/templates/styles.css +83 -0
  69. package/skills/magpie/tsconfig.json +1 -1
  70. package/skills/migrate/SKILL.md +28 -3
  71. package/skills/migrate/docs/architecture.md +19 -7
  72. package/skills/migrate/docs/reference.md +19 -13
  73. package/skills/migrate/evals/README.md +168 -0
  74. package/skills/migrate/evals/adjudicate-drafts-not-decides/case.yaml +4 -0
  75. package/skills/migrate/evals/adjudicate-drafts-not-decides/fixture.sh +14 -0
  76. package/skills/migrate/evals/adjudicate-drafts-not-decides/graders/drafts-presented-to-owner.md +10 -0
  77. package/skills/migrate/evals/adjudicate-drafts-not-decides/graders/no-ruling-recorded.md +11 -0
  78. package/skills/migrate/evals/adjudicate-drafts-not-decides/graders/q-express-mailer-delivery-unobservable-still-open.md +8 -0
  79. package/skills/migrate/evals/adjudicate-drafts-not-decides/graders/q-express-user-list-source-still-open.md +8 -0
  80. package/skills/migrate/evals/adjudicate-drafts-not-decides/graders/q-express-users-table-unwired-still-open.md +8 -0
  81. package/skills/migrate/evals/adjudicate-drafts-not-decides/graders/q-tiny-express-enumerate-scaffold-still-open.md +8 -0
  82. package/skills/migrate/evals/adjudicate-drafts-not-decides/graders/review-sheet-read.md +8 -0
  83. package/skills/migrate/evals/adjudicate-drafts-not-decides/graders/skill-fired.md +5 -0
  84. package/skills/migrate/evals/adjudicate-drafts-not-decides/prompt.md +11 -0
  85. package/skills/migrate/evals/done-means-plain-check/case.yaml +4 -0
  86. package/skills/migrate/evals/done-means-plain-check/fixture.sh +38 -0
  87. package/skills/migrate/evals/done-means-plain-check/graders/answers-not-finished.md +10 -0
  88. package/skills/migrate/evals/done-means-plain-check/graders/ran-plain-check.md +10 -0
  89. package/skills/migrate/evals/done-means-plain-check/graders/skill-fired.md +5 -0
  90. package/skills/migrate/evals/done-means-plain-check/graders/unsigned-delta-was-in-place.md +7 -0
  91. package/skills/migrate/evals/done-means-plain-check/prompt.md +11 -0
  92. package/skills/migrate/evals/extract-files-queue-in-same-pass/case.yaml +4 -0
  93. package/skills/migrate/evals/extract-files-queue-in-same-pass/enumerate/admin-router.js +7 -0
  94. package/skills/migrate/evals/extract-files-queue-in-same-pass/enumerate/census-routes.json +21 -0
  95. package/skills/migrate/evals/extract-files-queue-in-same-pass/enumerate/elements-dead-route.json +16 -0
  96. package/skills/migrate/evals/extract-files-queue-in-same-pass/fixture.sh +41 -0
  97. package/skills/migrate/evals/extract-files-queue-in-same-pass/graders/check-was-run.md +8 -0
  98. package/skills/migrate/evals/extract-files-queue-in-same-pass/graders/dead-route-accounted-for.md +13 -0
  99. package/skills/migrate/evals/extract-files-queue-in-same-pass/graders/no-check-saw-a-dangling-queue-id.md +12 -0
  100. package/skills/migrate/evals/extract-files-queue-in-same-pass/graders/queue-item-filed.md +5 -0
  101. package/skills/migrate/evals/extract-files-queue-in-same-pass/graders/skill-fired.md +5 -0
  102. package/skills/migrate/evals/extract-files-queue-in-same-pass/prompt.md +11 -0
  103. package/skills/migrate/evals/forecast-refuses-without-attestation/case.yaml +4 -0
  104. package/skills/migrate/evals/forecast-refuses-without-attestation/fixture.sh +10 -0
  105. package/skills/migrate/evals/forecast-refuses-without-attestation/graders/asks-owner-for-assumptions.md +10 -0
  106. package/skills/migrate/evals/forecast-refuses-without-attestation/graders/assumptions-not-written.md +8 -0
  107. package/skills/migrate/evals/forecast-refuses-without-attestation/graders/handoff-was-scaffolded.md +5 -0
  108. package/skills/migrate/evals/forecast-refuses-without-attestation/graders/skill-fired.md +5 -0
  109. package/skills/migrate/evals/forecast-refuses-without-attestation/prompt.md +11 -0
  110. package/skills/migrate/evals/handoff-blocked-reports-blockers/case.yaml +4 -0
  111. package/skills/migrate/evals/handoff-blocked-reports-blockers/fixture.sh +23 -0
  112. package/skills/migrate/evals/handoff-blocked-reports-blockers/graders/handoff-attempted.md +8 -0
  113. package/skills/migrate/evals/handoff-blocked-reports-blockers/graders/handoff-json-not-written.md +8 -0
  114. package/skills/migrate/evals/handoff-blocked-reports-blockers/graders/reopened-item-still-open.md +8 -0
  115. package/skills/migrate/evals/handoff-blocked-reports-blockers/graders/reports-the-blocker.md +10 -0
  116. package/skills/migrate/evals/handoff-blocked-reports-blockers/graders/skill-fired.md +5 -0
  117. package/skills/migrate/evals/handoff-blocked-reports-blockers/prompt.md +11 -0
  118. package/skills/migrate/evals/lib/express/01-init.sh +17 -0
  119. package/skills/migrate/evals/lib/express/02-phase-probe-done.sh +1 -0
  120. package/skills/migrate/evals/lib/express/03-queue-q-tiny-express-enumerate-scaffold.md +27 -0
  121. package/skills/migrate/evals/lib/express/04-import-elements-routes.json +61 -0
  122. package/skills/migrate/evals/lib/express/05-import-elements-tables.json +36 -0
  123. package/skills/migrate/evals/lib/express/06-import-elements-jobs.json +26 -0
  124. package/skills/migrate/evals/lib/express/07-import-elements-reports.json +26 -0
  125. package/skills/migrate/evals/lib/express/08-import-elements-screens.json +21 -0
  126. package/skills/migrate/evals/lib/express/09-import-elements-integrations.json +21 -0
  127. package/skills/migrate/evals/lib/express/10-import-elements-workflows.json +34 -0
  128. package/skills/migrate/evals/lib/express/11-import-elements-settings.json +36 -0
  129. package/skills/migrate/evals/lib/express/12-census-lens-routes.json +21 -0
  130. package/skills/migrate/evals/lib/express/13-census-lens-tables.json +21 -0
  131. package/skills/migrate/evals/lib/express/14-census-lens-jobs.json +21 -0
  132. package/skills/migrate/evals/lib/express/15-census-lens-reports.json +21 -0
  133. package/skills/migrate/evals/lib/express/16-census-lens-screens.json +21 -0
  134. package/skills/migrate/evals/lib/express/17-census-lens-integrations.json +21 -0
  135. package/skills/migrate/evals/lib/express/18-census-lens-workflows.json +21 -0
  136. package/skills/migrate/evals/lib/express/19-census-lens-settings.json +21 -0
  137. package/skills/migrate/evals/lib/express/20-phase-enumerate-done.sh +1 -0
  138. package/skills/migrate/evals/lib/express/21-seam-artifacts.sh +40 -0
  139. package/skills/migrate/evals/lib/express/22-phase-seam-done.sh +1 -0
  140. package/skills/migrate/evals/lib/express/23-import-reqs-extract.json +336 -0
  141. package/skills/migrate/evals/lib/express/24-queue-q-express-user-list-source.md +24 -0
  142. package/skills/migrate/evals/lib/express/25-queue-q-express-mailer-delivery-unobservable.md +24 -0
  143. package/skills/migrate/evals/lib/express/26-queue-q-express-users-table-unwired.md +26 -0
  144. package/skills/migrate/evals/lib/express/27-import-elements-disposed.json +219 -0
  145. package/skills/migrate/evals/lib/express/28-census-rule-sweep-user-directory.json +10 -0
  146. package/skills/migrate/evals/lib/express/29-census-rule-sweep-welcome-notification.json +10 -0
  147. package/skills/migrate/evals/lib/express/30-census-rule-sweep-audit-retention.json +10 -0
  148. package/skills/migrate/evals/lib/express/31-census-attribute-table-users.json +21 -0
  149. package/skills/migrate/evals/lib/express/32-census-closer-cross-capability-workflow.json +10 -0
  150. package/skills/migrate/evals/lib/express/33-census-closer-scope-injection.json +10 -0
  151. package/skills/migrate/evals/lib/express/34-census-closer-read-write-symmetry.json +12 -0
  152. package/skills/migrate/evals/lib/express/35-phase-extract-done.sh +1 -0
  153. package/skills/migrate/evals/lib/express/36-import-deltas-parity.json +14 -0
  154. package/skills/migrate/evals/lib/express/37-import-reqs-parity.json +367 -0
  155. package/skills/migrate/evals/lib/express/38-phase-parity-done.sh +1 -0
  156. package/skills/migrate/evals/lib/express/39-phase-queue-done.sh +1 -0
  157. package/skills/migrate/evals/lib/express/40-adjudicate-rulings.sh +4 -0
  158. package/skills/migrate/evals/lib/express/41-phase-adjudicate-done.sh +1 -0
  159. package/skills/migrate/evals/lib/express/42-handoff.sh +1 -0
  160. package/skills/migrate/evals/lib/express/43-phase-handoff-done.sh +1 -0
  161. package/skills/migrate/evals/lib/scaffold.sh +118 -0
  162. package/skills/migrate/evals/probe-asks-for-the-target/case.yaml +4 -0
  163. package/skills/migrate/evals/probe-asks-for-the-target/fixture.sh +11 -0
  164. package/skills/migrate/evals/probe-asks-for-the-target/graders/asks-for-the-target-profile.md +10 -0
  165. package/skills/migrate/evals/probe-asks-for-the-target/graders/init-waits-for-the-answers.md +11 -0
  166. package/skills/migrate/evals/probe-asks-for-the-target/graders/parity-basis-was-written.md +6 -0
  167. package/skills/migrate/evals/probe-asks-for-the-target/graders/skill-fired.md +5 -0
  168. package/skills/migrate/evals/probe-asks-for-the-target/graders/source-was-scaffolded.md +7 -0
  169. package/skills/migrate/evals/probe-asks-for-the-target/prompt.md +11 -0
  170. package/skills/migrate/evals/resume-reads-status-first/case.yaml +4 -0
  171. package/skills/migrate/evals/resume-reads-status-first/fixture.sh +44 -0
  172. package/skills/migrate/evals/resume-reads-status-first/graders/config-left-as-it-was.md +8 -0
  173. package/skills/migrate/evals/resume-reads-status-first/graders/never-reruns-init.md +12 -0
  174. package/skills/migrate/evals/resume-reads-status-first/graders/reads-status-before-writing.md +12 -0
  175. package/skills/migrate/evals/resume-reads-status-first/graders/skill-fired.md +5 -0
  176. package/skills/migrate/evals/resume-reads-status-first/prompt.md +11 -0
  177. package/skills/migrate/evals/seam-low-q-escalates/case.yaml +4 -0
  178. package/skills/migrate/evals/seam-low-q-escalates/enumerate/census-integrations.json +21 -0
  179. package/skills/migrate/evals/seam-low-q-escalates/enumerate/census-jobs.json +21 -0
  180. package/skills/migrate/evals/seam-low-q-escalates/enumerate/census-reports.json +21 -0
  181. package/skills/migrate/evals/seam-low-q-escalates/enumerate/census-routes.json +21 -0
  182. package/skills/migrate/evals/seam-low-q-escalates/enumerate/census-screens.json +21 -0
  183. package/skills/migrate/evals/seam-low-q-escalates/enumerate/census-settings.json +21 -0
  184. package/skills/migrate/evals/seam-low-q-escalates/enumerate/census-tables.json +21 -0
  185. package/skills/migrate/evals/seam-low-q-escalates/enumerate/census-workflows.json +21 -0
  186. package/skills/migrate/evals/seam-low-q-escalates/enumerate/elements.json +183 -0
  187. package/skills/migrate/evals/seam-low-q-escalates/fixture.sh +48 -0
  188. package/skills/migrate/evals/seam-low-q-escalates/graders/hands-the-choice-to-the-owner.md +10 -0
  189. package/skills/migrate/evals/seam-low-q-escalates/graders/item-is-critical-and-offers-vertical.md +9 -0
  190. package/skills/migrate/evals/seam-low-q-escalates/graders/no-partition-written.md +10 -0
  191. package/skills/migrate/evals/seam-low-q-escalates/graders/queue-item-filed.md +5 -0
  192. package/skills/migrate/evals/seam-low-q-escalates/graders/seam-not-closed.md +9 -0
  193. package/skills/migrate/evals/seam-low-q-escalates/graders/skill-fired.md +5 -0
  194. package/skills/migrate/evals/seam-low-q-escalates/graders/store-was-scaffolded.md +8 -0
  195. package/skills/migrate/evals/seam-low-q-escalates/legacy/schema.sql +20 -0
  196. package/skills/migrate/evals/seam-low-q-escalates/legacy/server.js +76 -0
  197. package/skills/migrate/evals/seam-low-q-escalates/prompt.md +11 -0
  198. package/skills/migrate/evals/seam-shared-element-queued/case.yaml +4 -0
  199. package/skills/migrate/evals/seam-shared-element-queued/enumerate/census-integrations.json +25 -0
  200. package/skills/migrate/evals/seam-shared-element-queued/enumerate/census-jobs.json +25 -0
  201. package/skills/migrate/evals/seam-shared-element-queued/enumerate/census-reports.json +25 -0
  202. package/skills/migrate/evals/seam-shared-element-queued/enumerate/census-routes.json +25 -0
  203. package/skills/migrate/evals/seam-shared-element-queued/enumerate/census-screens.json +21 -0
  204. package/skills/migrate/evals/seam-shared-element-queued/enumerate/census-settings.json +21 -0
  205. package/skills/migrate/evals/seam-shared-element-queued/enumerate/census-tables.json +25 -0
  206. package/skills/migrate/evals/seam-shared-element-queued/enumerate/census-workflows.json +21 -0
  207. package/skills/migrate/evals/seam-shared-element-queued/enumerate/elements.json +278 -0
  208. package/skills/migrate/evals/seam-shared-element-queued/fixture.sh +34 -0
  209. package/skills/migrate/evals/seam-shared-element-queued/graders/shared-setting-in-no-capability.md +10 -0
  210. package/skills/migrate/evals/seam-shared-element-queued/graders/shared-setting-queued.md +10 -0
  211. package/skills/migrate/evals/seam-shared-element-queued/graders/skill-fired.md +5 -0
  212. package/skills/migrate/evals/seam-shared-element-queued/graders/store-was-scaffolded.md +8 -0
  213. package/skills/migrate/evals/seam-shared-element-queued/prompt.md +11 -0
  214. package/skills/migrate/evals/stale-lock-not-forced/case.yaml +4 -0
  215. package/skills/migrate/evals/stale-lock-not-forced/fixture.sh +43 -0
  216. package/skills/migrate/evals/stale-lock-not-forced/graders/batch-was-supplied.md +6 -0
  217. package/skills/migrate/evals/stale-lock-not-forced/graders/lock-left-in-place.md +8 -0
  218. package/skills/migrate/evals/stale-lock-not-forced/graders/lock-not-forced.md +11 -0
  219. package/skills/migrate/evals/stale-lock-not-forced/graders/names-the-lock-holder.md +10 -0
  220. package/skills/migrate/evals/stale-lock-not-forced/graders/skill-fired.md +5 -0
  221. package/skills/migrate/evals/stale-lock-not-forced/prompt.md +11 -0
  222. package/skills/migrate/package.json +1 -1
  223. package/skills/migrate/references/phases/adjudicate.md +6 -3
  224. package/skills/migrate/references/phases/enumerate.md +18 -8
  225. package/skills/migrate/references/phases/extract.md +54 -23
  226. package/skills/migrate/references/phases/parity.md +36 -24
  227. package/skills/migrate/references/phases/probe.md +25 -12
  228. package/skills/migrate/references/phases/queue.md +29 -23
  229. package/skills/migrate/references/phases/seam.md +46 -29
  230. package/skills/migrate/scripts/__tests__/check.test.ts +347 -5
  231. package/skills/migrate/scripts/__tests__/e2e.test.ts +17 -1
  232. package/skills/migrate/scripts/__tests__/eval-scaffold.test.ts +130 -0
  233. package/skills/migrate/scripts/__tests__/gates-handoff.test.ts +10 -0
  234. package/skills/migrate/scripts/__tests__/handoff-cmd.test.ts +10 -0
  235. package/skills/migrate/scripts/__tests__/init.test.ts +32 -0
  236. package/skills/migrate/scripts/__tests__/phase-cmd.test.ts +39 -0
  237. package/skills/migrate/scripts/__tests__/phases.test.ts +1 -0
  238. package/skills/migrate/scripts/__tests__/status-reset.test.ts +14 -4
  239. package/skills/migrate/scripts/census.ts +10 -8
  240. package/skills/migrate/scripts/config.ts +4 -3
  241. package/skills/migrate/scripts/gates/census.ts +33 -5
  242. package/skills/migrate/scripts/gates/parity.ts +29 -0
  243. package/skills/migrate/scripts/gates/refs.ts +80 -1
  244. package/skills/migrate/scripts/init-cmd.ts +8 -2
  245. package/skills/migrate/scripts/phase-cmd.ts +14 -1
  246. package/skills/migrate/scripts/phases.ts +27 -0
  247. package/skills/migrate/skill.json +1 -1
  248. package/skills/terse/SKILL.md +1 -0
  249. package/skills/terse/bench/README.md +196 -0
  250. package/skills/terse/bench/bench.ts +431 -0
  251. package/skills/terse/bench/prompts.json +181 -0
  252. package/skills/terse/bench/stats.ts +131 -0
  253. package/skills/terse/evals/README.md +152 -0
  254. package/skills/terse/evals/commit-message-written-normally/graders/message-is-plain-prose.md +12 -0
  255. package/skills/terse/evals/commit-message-written-normally/graders/no-preamble.md +7 -0
  256. package/skills/terse/evals/commit-message-written-normally/graders/skill-fired.md +5 -0
  257. package/skills/terse/evals/commit-message-written-normally/prompt.md +30 -0
  258. package/skills/terse/evals/explanation-has-no-recap/graders/explains-shared-default.md +10 -0
  259. package/skills/terse/evals/explanation-has-no-recap/graders/no-closing-recap.md +7 -0
  260. package/skills/terse/evals/explanation-has-no-recap/graders/no-preamble.md +7 -0
  261. package/skills/terse/evals/explanation-has-no-recap/graders/no-question-echo.md +7 -0
  262. package/skills/terse/evals/explanation-has-no-recap/graders/skill-fired.md +5 -0
  263. package/skills/terse/evals/explanation-has-no-recap/prompt.md +10 -0
  264. package/skills/terse/evals/fix-reports-the-cause/case.yaml +4 -0
  265. package/skills/terse/evals/fix-reports-the-cause/fixture.sh +16 -0
  266. package/skills/terse/evals/fix-reports-the-cause/graders/fixture-scaffolded.md +5 -0
  267. package/skills/terse/evals/fix-reports-the-cause/graders/handback-states-cause-not-steps.md +10 -0
  268. package/skills/terse/evals/fix-reports-the-cause/graders/skill-fired.md +5 -0
  269. package/skills/terse/evals/fix-reports-the-cause/graders/tax-on-discounted-subtotal.md +10 -0
  270. package/skills/terse/evals/fix-reports-the-cause/prompt.md +11 -0
  271. package/skills/terse/evals/plain-register/graders/gives-usable-advice.md +10 -0
  272. package/skills/terse/evals/plain-register/graders/no-consultant-words.md +10 -0
  273. package/skills/terse/evals/plain-register/graders/no-preamble.md +7 -0
  274. package/skills/terse/evals/plain-register/graders/skill-fired.md +5 -0
  275. package/skills/terse/evals/plain-register/prompt.md +10 -0
  276. package/skills/terse/evals/recommendation-leads-and-holds/graders/adds-nothing-unasked.md +12 -0
  277. package/skills/terse/evals/recommendation-leads-and-holds/graders/keeps-a-real-caveat.md +9 -0
  278. package/skills/terse/evals/recommendation-leads-and-holds/graders/no-preamble.md +7 -0
  279. package/skills/terse/evals/recommendation-leads-and-holds/graders/recommends-sqlite-first.md +10 -0
  280. package/skills/terse/evals/recommendation-leads-and-holds/graders/skill-fired.md +5 -0
  281. package/skills/terse/evals/recommendation-leads-and-holds/prompt.md +10 -0
  282. package/skills/terse/evals/security-warning-survives-sharp/graders/no-preamble.md +7 -0
  283. package/skills/terse/evals/security-warning-survives-sharp/graders/skill-fired.md +5 -0
  284. package/skills/terse/evals/security-warning-survives-sharp/graders/warns-in-full-sentences.md +10 -0
  285. package/skills/terse/evals/security-warning-survives-sharp/prompt.md +10 -0
  286. package/skills/terse/evals/sharp-compresses-structure/graders/keeps-the-mechanism.md +10 -0
  287. package/skills/terse/evals/sharp-compresses-structure/graders/no-preamble.md +7 -0
  288. package/skills/terse/evals/sharp-compresses-structure/graders/skill-fired.md +5 -0
  289. package/skills/terse/evals/sharp-compresses-structure/graders/stays-under-120-words.md +7 -0
  290. package/skills/terse/evals/sharp-compresses-structure/graders/uses-arrows.md +5 -0
  291. package/skills/terse/evals/sharp-compresses-structure/prompt.md +10 -0
  292. package/skills/terse/evals/short-answer-stays-short/graders/no-preamble.md +7 -0
  293. package/skills/terse/evals/short-answer-stays-short/graders/skill-fired.md +5 -0
  294. package/skills/terse/evals/short-answer-stays-short/graders/states-log-n.md +7 -0
  295. package/skills/terse/evals/short-answer-stays-short/graders/stays-under-forty-words.md +10 -0
  296. package/skills/terse/evals/short-answer-stays-short/prompt.md +10 -0
  297. package/skills/terse/skill.json +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iceinvein/agent-skills",
3
- "version": "0.21.1",
3
+ "version": "0.21.4",
4
4
  "description": "Install agent skills into AI coding tools",
5
5
  "author": "iceinvein",
6
6
  "license": "MIT",
@@ -11,7 +11,8 @@
11
11
  "files": [
12
12
  "dist",
13
13
  "skills",
14
- "!skills/*/evals/results"
14
+ "!skills/*/evals/results",
15
+ "!skills/terse/bench/results"
15
16
  ],
16
17
  "scripts": {
17
18
  "build": "bun build src/cli/index.ts --outdir dist/cli --target bun",
package/skills/index.json CHANGED
@@ -219,15 +219,15 @@
219
219
  },
220
220
  {
221
221
  "name": "magpie",
222
- "description": "Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture), dedupes findings, applies a critic rubric, peer-reviews via codex exec (falling back to a Claude second opinion when codex is unavailable), and serves an interactive HTML report for selecting findings to post via gh. Bundles a Bun CLI installed onto PATH via the skill's postinstall step. Use when the user asks to review a GitHub pull request. Splits oversized diffs into budgeted shards and rebuilds the diff from the local clone when gh pr diff refuses it.",
222
+ "description": "Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture), drops findings whose quoted evidence is not in the code, dedupes, runs critic subagents that re-check each finding against the worktree and set its risk label, peer-reviews via codex exec (falling back to a Claude second opinion when codex is unavailable), and serves an interactive HTML report for selecting or dismissing findings to post via gh, recording the outcome per finding as labels. Bundles a Bun CLI installed onto PATH via the skill's postinstall step. Use when the user asks to review a GitHub pull request. Splits oversized diffs into budgeted shards and rebuilds the diff from the local clone when gh pr diff refuses it.",
223
223
  "type": "prompt",
224
- "version": "0.12.0"
224
+ "version": "0.13.0"
225
225
  },
226
226
  {
227
227
  "name": "migrate",
228
228
  "description": "Source-agnostic legacy migration mapping. Walks a legacy codebase through probe, enumerate, seam, extract, parity, queue, adjudicate and handoff, building an auditable requirements ledger with mandatory citations and a `migrate check` gate in place of self-reported completeness. Use when the user asks to migrate, re-specify, replatform, or map a legacy system onto a new stack, to hand mapped requirements to a delivery team, or to resume, check, report, or forecast a mapping run already under way.",
229
229
  "type": "prompt",
230
- "version": "0.3.0"
230
+ "version": "0.4.0"
231
231
  },
232
232
  {
233
233
  "name": "module-secret-auditor",
@@ -299,7 +299,7 @@
299
299
  "name": "terse",
300
300
  "description": "Professional output compression. Cuts ~20-30% of output tokens with proper grammar, a plain-language register, and semantic accuracy. Three levels: clean, tight, sharp.",
301
301
  "type": "prompt",
302
- "version": "1.3.1"
302
+ "version": "1.3.3"
303
303
  },
304
304
  {
305
305
  "name": "type-driven-designer",
@@ -4,7 +4,7 @@ Interactive Claude Code skill that runs a multi-stage PR review pipeline inside
4
4
 
5
5
  ## What it does
6
6
 
7
- Given a GitHub PR number, dispatches five specialist subagents in parallel (security, bugs, performance, code-smells, architecture), dedupes their findings, applies a critic rubric, peer-reviews via `codex exec` (falling back to a Claude second-opinion subagent when codex is unavailable), serves an interactive HTML report, and posts the findings the user selects via `gh`.
7
+ Given a GitHub PR number, dispatches five specialist subagents in parallel (security, bugs, performance, code-smells, architecture), each quoting an evidence snippet for every anchored finding. Dedupe drops findings whose snippet is not in the file and groups nearby findings from different domains as merge candidates. Critic subagents then re-check each surviving finding against the worktree, keep, drop or merge it, and set its risk label, from which the severity is derived. Peer review runs via `codex exec` (falling back to a Claude second-opinion subagent when codex is unavailable). The interactive HTML report puts the top recommended findings first, lets the user dismiss findings with a reason, and posts the ones they select via `gh`; cleanup records what was posted, dismissed or ignored in `labels.json`.
8
8
 
9
9
  ## Requirements
10
10
 
@@ -54,7 +54,7 @@ The fixture lives at `fixtures/example-pr/` (pr.json + findings.final.json + pos
54
54
  ## Layout
55
55
 
56
56
  - `SKILL.md` is the agent-facing prompt: the stage walkthrough and nothing else. Installed by the agent-skills CLI.
57
- - `references/` holds the prompt bodies the walkthrough loads on demand, one file per stage that needs one: `scout.md` (stage 3, the PR-brief prompt and the `brief.json` contract), `specialists.md` (stage 4, the five focus blocks plus the shared output contract), `critic.md` (stage 6), `peer-review.md` (stage 7, including the Claude-fallback preamble). They ship in the bundle and sit next to `SKILL.md` once installed.
57
+ - `references/` holds the prompt bodies the walkthrough loads on demand, one file per stage that needs one: `scout.md` (stage 3, the PR-brief prompt and the `brief.json` contract, including the repository review rules), `specialists.md` (stage 4, the five focus blocks plus the shared output contract), `critic.md` (stage 6, the critic subagent prompt `magpie critic-prompt` fills), `peer-review.md` (stage 7, including the Claude-fallback preamble). They ship in the bundle and sit next to `SKILL.md` once installed.
58
58
  - `skill.json` is the agent-skills manifest.
59
59
  - `bin/magpie` is the CLI invoked by the agent during stages; symlinked onto PATH by `install.sh`.
60
60
  - `scripts/` holds the implementation (server, dedupe, render, setup, cleanup, etc.).
@@ -66,4 +66,4 @@ The fixture lives at `fixtures/example-pr/` (pr.json + findings.final.json + pos
66
66
 
67
67
  ## Run directory layout
68
68
 
69
- Each invocation creates `~/.magpie/pr-<n>-<ts>/` with `pr.json`, `diff.patch`, `findings/`, `findings.deduped.json`, `findings.kept.json`, `findings.final.json`, `screen/`, `state/`, `log.jsonl`. On completion the directory is renamed to `<run-dir>.archived-<timestamp>` rather than deleted, so logs survive for postmortem.
69
+ Each invocation creates `~/.magpie/pr-<n>-<ts>/` with `pr.json`, `diff.patch`, `findings/`, `findings.deduped.json`, `merge-candidates.json`, `critic-prompt.md` and `critic.json` (numbered per batch when there is more than one), `findings.kept.json`, `critic-dropped.json`, `findings.final.json`, `labels.json`, `screen/`, `state/`, `log.jsonl`. On completion the directory is renamed to `<run-dir>.archived-<timestamp>` rather than deleted, so logs survive for postmortem.
@@ -148,7 +148,9 @@ If every specialist fails (no findings files written), log
148
148
  magpie dedupe "$RUN_DIR" [--threshold <0-10>]
149
149
  ```
150
150
 
151
- `magpie dedupe` also runs a deterministic evidence check against the worktree: findings whose `file` is missing or whose `line` is out of range are dropped, logged, and recorded to `$RUN_DIR/evidence-dropped.json`. The check is skipped when the worktree is gone (archived run replay).
151
+ `magpie dedupe` also checks evidence against the worktree, dropping an anchored finding whose file is missing (`hallucinated-file`), whose line is out of range (`invented-line`), that has no `evidence` snippet (`missing-evidence`), or whose snippet is not near its line (`evidence-not-found`); a snippet found exactly once elsewhere in the file re-anchors it. Both go to `$RUN_DIR/evidence-dropped.json` as `{dropped, reanchored}`, written only when non-empty, so no file is normal. The check is skipped when the worktree is gone (archived run replay).
152
+
153
+ It always writes `$RUN_DIR/merge-candidates.json`: ids from different domains anchored close together in one file, for the critic to merge or keep apart.
152
154
 
153
155
  Each finding gets a derived 0-10 `score` from its risk fields; those below `--threshold` (default 3) are dropped before the critic LLM runs and recorded to `$RUN_DIR/threshold-dropped.json`. Pass `--threshold 0` to keep everything.
154
156
 
@@ -156,12 +158,21 @@ Re-render progress.
156
158
 
157
159
  ### 6. Critic
158
160
 
159
- Read `references/critic.md` and `$RUN_DIR/findings.deduped.json`. Substitute both placeholders in the critic rubric (the compact candidate list including each finding's `onChangedLine`, and the `<<DIFF_EXCERPT>>` hunks for the referenced files), then apply the rubric verbatim (one verdict per finding). Write the kept subset to `$RUN_DIR/findings.kept.json`. Append `{stage: critic, status: done}` and re-render progress.
161
+ The critic runs as worktree-reading subagents. The CLI fills its prompt from `references/critic.md`; substitute nothing by hand. Append `{stage: critic, status: running}` to `$RUN_DIR/log.jsonl`, re-render progress, then:
162
+
163
+ ```
164
+ magpie critic-prompt "$RUN_DIR"
165
+ ```
166
+
167
+ It prints `<prompt path>\t<output path>` per batch of about 30 candidates (a merge group always stays in one batch). Dispatch one subagent (Agent tool, `general-purpose`) per line, all in one message, each one's entire task the verbatim contents of its prompt file. Each writes its output path and returns one summary line. No lines printed means no candidates: go straight to `critic-apply`.
160
168
 
161
- When `findings.deduped.json` holds more than 40 findings, run the rubric in batches of
162
- 30 rather than one prompt: a sharded run can produce more candidates than fit alongside
163
- their diff excerpts. Apply the same rubric verbatim per batch and concatenate the kept
164
- subsets into `findings.kept.json`.
169
+ Confirm every output path exists; re-dispatch any batch whose file is missing. On a resume, dispatch only the batches whose output file is missing. Apply the verdicts:
170
+
171
+ ```
172
+ magpie critic-apply "$RUN_DIR"
173
+ ```
174
+
175
+ It writes `findings.kept.json` and `critic-dropped.json` and logs the critic `done` entry itself; do not append another. On a non-zero exit, stderr names offending ids or a file: delete the output file of each batch whose ids or file it names, re-dispatch only those, and re-run `critic-apply`. If a batch fails twice after re-dispatch, stop and show the user stderr verbatim rather than looping. Re-render progress.
165
176
 
166
177
  ### 7. Peer review
167
178
 
@@ -169,7 +180,7 @@ Append `{stage: peer-review, status: running}` to `$RUN_DIR/log.jsonl` and re-re
169
180
 
170
181
  Build the peer-review prompt first: read `references/peer-review.md`, take the `magpie-peer-review` block from it, and substitute the placeholders listed in that file's `## Substitute before use` preamble.
171
182
 
172
- One batch carries up to 40 findings; above that, split them 30 at a time, as in stage 6.
183
+ One batch carries up to 40 findings; above that, split them 30 at a time.
173
184
  Write each batch's prompt, its `<<KEPT_FINDINGS_COMPACT>>` narrowed to that batch, to
174
185
  `$RUN_DIR/peer-prompt-<k>.md`, `<k>` counting from 1. **When there is a single batch, drop `-<k>` throughout** (`peer-prompt.md`,
175
186
  `peer.out`), which is the common case. Keep the `add` id counter running across batches
@@ -189,7 +200,7 @@ If codex returns non-zero on a batch, do not abort: record `{stage: peer-review,
189
200
 
190
201
  **Claude path (fallback).** When `codex` is unavailable or failed, get the second opinion from a Claude subagent instead, one per batch. Set `<<PEER_PROVIDER>>` to `claude`, then prepend the `magpie-peer-review-claude-preamble` block from `references/peer-review.md` to each batch's substituted prompt (the preamble forces genuine independence, since the reviewer shares a model family with the primary reviewers). Dispatch one subagent (Agent tool, `general-purpose`) per batch whose entire task is that combined prompt, and instruct it to return only the fenced `review-peer-review` JSON block. Write each output to `$RUN_DIR/peer-<k>.out`, extract each `review-peer-review` block, merge into `$RUN_DIR/peer.json` after the last batch as above, and append `{stage: peer-review, status: done, provider: claude}` (`provider: mixed` if codex handled some batches).
191
202
 
192
- **Apply the verdicts (both paths).** Parse the merged verdicts and apply the `update` / `add` entries (an empty array means no change). Mint each `add`'s `id` as above before merging, since the peer contract does not carry ids. Then write `findings.final.json`. Re-render progress.
203
+ **Apply the verdicts (both paths).** Parse the merged verdicts and apply the `update` / `add` entries (an empty array means no change). A peer `fields.severity` (or `finding.severity` on an `add`) is ignored: severity is derived from `risk.impact`. Mint each `add`'s `id` as above before merging, since the peer contract does not carry ids. Then write `findings.final.json`. Re-render progress.
193
204
 
194
205
  ### 8. Report
195
206
 
@@ -197,6 +208,8 @@ If codex returns non-zero on a batch, do not abort: record `{stage: peer-review,
197
208
  magpie render "$RUN_DIR" findings
198
209
  ```
199
210
 
211
+ The report shows the top 10 recommended (`must-fix`/`should-fix`, highest score first) and folds the rest. `--top <n>` is not saved (the stage 9 re-render and `magpie serve`'s auto-refresh show 10 again), so use it only when nothing will re-render.
212
+
200
213
  Append `{stage: report, status: done}` to `$RUN_DIR/log.jsonl` and re-render progress (the render CLI does not log this itself, and `magpie status` needs the `done` entry to resume past `report`).
201
214
 
202
215
  Print to the terminal: "Findings ready at <url>. Tick the ones you want and click **Post Selected**, or reply `post` here and I'll post whatever you've ticked."
@@ -205,9 +218,9 @@ End the turn.
205
218
 
206
219
  ### 9. Post
207
220
 
208
- Most users tick the checkboxes in the served report and click **Post Selected** (or **Post Recommended**, which takes every `must-fix`/`should-fix` finding and skips the `consider`/`optional` ones); the server posts that batch as one GitHub review with inline threads. The agent posts only when the user types `post` (optionally `post 1,3,7` for indices), which takes the CLI path below: separate inline comments plus a top-level summary comment. Either path records posted ids in `post-status.json`, so the two cannot double-post the same finding.
221
+ Most users tick the checkboxes in the served report and click **Post Selected** (or **Post Recommended**, which takes only the top N above the fold; **Select recommended** ticks the same set); the server posts that batch as one GitHub review with inline threads. **Dismiss** on a finding records a reason (`wrong`, `not-worth-it`, `duplicate`, `style`) and drops it from the recommended set. The agent posts only when the user types `post` (optionally `post 1,3,7` for indices), which takes the CLI path below: separate inline comments plus a top-level summary comment. Either path records posted ids in `post-status.json`, so the two cannot double-post the same finding.
209
222
 
210
- When the user types `post`, read `$RUN_DIR/state/events` and fold them in order, keeping the LAST event per finding id; ids whose last event is `select` are selected. (Not union-minus: the UI emits one event per toggle, so select, deselect, select again resolves to selected.) Merge any explicit indices the user named (1-based, against `findings.final.json` in file order). If nothing is selected, say so and ask rather than posting an empty batch. Then post via the CLI:
223
+ When the user types `post`, read `$RUN_DIR/state/events` and fold them in order, keeping the LAST event per finding id; ids whose last event is `select` are selected (a later `dismiss` therefore unselects). (Not union-minus: the UI emits one event per toggle, so select, deselect, select again resolves to selected.) Merge any explicit indices the user named (1-based, against `findings.final.json` in file order). If nothing is selected, say so and ask rather than posting an empty batch. Then post via the CLI:
211
224
 
212
225
  ```
213
226
  magpie post "$RUN_DIR" --ids id1,id2,id3
@@ -232,7 +245,7 @@ magpie render "$RUN_DIR" findings
232
245
  magpie cleanup "$RUN_DIR" --repo "$REPO"
233
246
  ```
234
247
 
235
- The run directory is renamed to `<run-dir>.archived-<timestamp>` and the worktree is removed. The CLI prints two lines on success: `archived to <path>` and `view later: magpie open <archived-id>`. Surface that second line verbatim so the user has a one-command path back to the report.
248
+ Cleanup first writes `labels.json` (each final finding posted, dismissed or ignored, with the post route or dismiss reason; `magpie labels "$RUN_DIR"` writes it on demand). The run directory is then renamed to `<run-dir>.archived-<timestamp>` and the worktree is removed. The CLI prints two lines on success: `archived to <path>` and `view later: magpie open <archived-id>`. Surface that second line verbatim so the user has a one-command path back to the report.
236
249
 
237
250
  The archived `findings.html` is self-contained and auto-switches to read-only "archived" mode when opened, so:
238
251
 
@@ -10,10 +10,18 @@ Subcommands:
10
10
  setup <run-dir> --pr <n> Pre-flight, fetch PR, create worktree
11
11
  serve <run-dir-or-id> Start the HTML server (accepts active or archived run id)
12
12
  dedupe <run-dir> Merge specialist findings into deduped set
13
+ critic-prompt <run-dir> [--batch-size N]
14
+ Write one critic prompt per batch, print prompt and output paths
15
+ critic-apply <run-dir> [--design-cap N]
16
+ Apply critic verdicts, write findings.kept.json (no cap on
17
+ code-smells + architecture keeps unless --design-cap is given)
13
18
  shard <run-dir> [--budget N] [--max-files N]
14
19
  Re-split diff.patch into budgeted shards
15
- render <run-dir> <page> Render progress.html or findings.html
16
- cleanup <run-dir> Remove worktree, stop server, archive run
20
+ render <run-dir> <page> [--top N]
21
+ Render progress.html or findings.html (findings recommends
22
+ the top N, default 10, and folds the rest)
23
+ labels <run-dir> Write labels.json (posted, dismissed, ignored per finding)
24
+ cleanup <run-dir> Remove worktree, stop server, write labels, archive run
17
25
  status <run-dir> Print highest completed stage
18
26
  open [id] Open findings.html in your browser (defaults to latest run)
19
27
  post <run-dir> --ids a,b Post the given finding ids via gh (rich body + optional summary)
@@ -92,9 +100,16 @@ const HANDLERS: Record<string, Handler> = {
92
100
  }
93
101
  }
94
102
  // Auto-refresh: re-render findings.html with the currently-shipped CSS/JS
95
- // so serving an old archive picks up new report features. Best-effort.
103
+ // so serving an old archive picks up new report features. A failed refresh
104
+ // leaves the previous page in place, so serve keeps going after reporting why.
96
105
  const { refreshFindings } = await import('../scripts/refresh.ts')
97
- await refreshFindings(runDir).catch(() => {})
106
+ try {
107
+ await refreshFindings(runDir)
108
+ } catch (err) {
109
+ process.stderr.write(
110
+ `serve: refresh failed, serving the previous page: ${err instanceof Error ? err.message : String(err)}\n`,
111
+ )
112
+ }
98
113
  const { runServe } = await import('../scripts/serve-cmd.ts')
99
114
  return runServe({ runDir, idleMs, host })
100
115
  },
@@ -118,6 +133,48 @@ const HANDLERS: Record<string, Handler> = {
118
133
  const { runDedupe } = await import('../scripts/dedupe-cmd.ts')
119
134
  return runDedupe(runDir, threshold !== undefined ? { threshold } : {})
120
135
  },
136
+ 'critic-prompt': async (args) => {
137
+ const runDir = args[0]
138
+ if (!runDir) {
139
+ process.stderr.write('critic-prompt: missing <run-dir> [--batch-size <n>]\n')
140
+ return 2
141
+ }
142
+ const flag = args.indexOf('--batch-size')
143
+ const raw = flag !== -1 ? args[flag + 1] : undefined
144
+ let batchSize: number | undefined
145
+ if (flag !== -1) {
146
+ const n = Number(raw)
147
+ if (!Number.isInteger(n) || n <= 0) {
148
+ process.stderr.write(
149
+ `critic-prompt: invalid --batch-size ${raw} (want a positive integer)\n`,
150
+ )
151
+ return 2
152
+ }
153
+ batchSize = n
154
+ }
155
+ const { runCriticPrompt } = await import('../scripts/critic-cmd.ts')
156
+ return runCriticPrompt(runDir, batchSize !== undefined ? { batchSize } : {})
157
+ },
158
+ 'critic-apply': async (args) => {
159
+ const runDir = args[0]
160
+ if (!runDir) {
161
+ process.stderr.write('critic-apply: missing <run-dir> [--design-cap <n>]\n')
162
+ return 2
163
+ }
164
+ const flag = args.indexOf('--design-cap')
165
+ const raw = flag !== -1 ? args[flag + 1] : undefined
166
+ let designCap: number | undefined
167
+ if (flag !== -1) {
168
+ const n = Number(raw)
169
+ if (!Number.isInteger(n) || n < 0) {
170
+ process.stderr.write(`critic-apply: invalid --design-cap ${raw} (want an integer >= 0)\n`)
171
+ return 2
172
+ }
173
+ designCap = n
174
+ }
175
+ const { runCriticApply } = await import('../scripts/critic-cmd.ts')
176
+ return runCriticApply(runDir, designCap !== undefined ? { designCap } : {})
177
+ },
121
178
  shard: async (args) => {
122
179
  const runDir = args[0]
123
180
  if (!runDir) {
@@ -163,11 +220,31 @@ const HANDLERS: Record<string, Handler> = {
163
220
  const runDir = args[0]
164
221
  const page = args[1]
165
222
  if (!runDir || (page !== 'progress' && page !== 'findings')) {
166
- process.stderr.write('render: missing <run-dir> <progress|findings>\n')
223
+ process.stderr.write('render: missing <run-dir> <progress|findings> [--top <n>]\n')
167
224
  return 2
168
225
  }
226
+ const flag = args.indexOf('--top')
227
+ const raw = flag !== -1 ? args[flag + 1] : undefined
228
+ let topN: number | undefined
229
+ if (flag !== -1) {
230
+ const n = Number(raw)
231
+ if (!Number.isInteger(n) || n <= 0) {
232
+ process.stderr.write(`render: invalid --top ${raw} (want a positive integer)\n`)
233
+ return 1
234
+ }
235
+ topN = n
236
+ }
169
237
  const { runRender } = await import('../scripts/render-cmd.ts')
170
- return runRender(runDir, page)
238
+ return runRender(runDir, page, topN !== undefined ? { topN } : {})
239
+ },
240
+ labels: async (args) => {
241
+ const runDir = args[0]
242
+ if (!runDir) {
243
+ process.stderr.write('labels: missing <run-dir>\n')
244
+ return 2
245
+ }
246
+ const { runLabels } = await import('../scripts/labels-cmd.ts')
247
+ return runLabels(runDir)
171
248
  },
172
249
  cleanup: async (args) => {
173
250
  const runDir = args[0]
@@ -248,6 +325,7 @@ const HANDLERS: Record<string, Handler> = {
248
325
  runDir,
249
326
  findingIds,
250
327
  dryRun,
328
+ via: 'cli',
251
329
  ...(includeSummary ? { includeSummary } : {}),
252
330
  })
253
331
  process.stdout.write(`${JSON.stringify(outcome)}\n`)
@@ -80,6 +80,21 @@ file is what most of the graders read: "posted exactly these ids", "never ran
80
80
  invoked, and the call log answers them without depending on how the reply is
81
81
  worded.
82
82
 
83
+ Cases that start past stage 6 hand the agent what the subagent critic would
84
+ have left. `codex-missing-falls-back` writes the whole chain: specialist files
85
+ with an `evidence` snippet per finding and no `severity`, `findings.deduped.json`
86
+ with severity derived from `risk.impact`, `threshold-dropped.json`,
87
+ `merge-candidates.json`, `critic.json` with one verdict per candidate,
88
+ `critic-dropped.json`, and the `findings.kept.json` that `magpie critic-apply`
89
+ produces from them. The log carries the critic `done` entry with the counts
90
+ critic-apply writes. Running the real `magpie dedupe` and `critic-apply` over
91
+ that run directory gives the same `merge-candidates.json`,
92
+ `threshold-dropped.json` and `critic-dropped.json`, and the same kept ids and
93
+ log counts. `findings.deduped.json` and `findings.kept.json` differ only in
94
+ `onChangedLine`, which the fixture leaves out, and in order; no grader reads
95
+ either. Repeat that check after changing either command. The report and post cases start from
96
+ `findings.final.json` and only need the same log entry and the evidence field.
97
+
83
98
  `fixture.sh` is duplicated across the cases rather than shared, because
84
99
  `context.scaffold_script` reads only from the case's own directory.
85
100
 
@@ -111,7 +111,8 @@ cat > "$RUN_DIR/log.jsonl" <<'LOG'
111
111
  {"stage":"context","status":"done"}
112
112
  {"stage":"specialists","status":"done"}
113
113
  {"stage":"dedupe","status":"done"}
114
- {"stage":"critic","status":"done"}
114
+ {"stage":"critic","status":"running"}
115
+ {"stage":"critic","status":"done","kept":3,"dropped":1,"merged":0,"capped":0}
115
116
  LOG
116
117
 
117
118
  # The PR under review, as setup would have left it: the filtered diff, and a
@@ -187,9 +188,10 @@ export async function load(tenantId: string) {
187
188
  TS
188
189
 
189
190
  # The findings the run already has: one file per specialist focus, the deduped
190
- # set derived from them, and the subset the critic kept. Generated together so
191
- # the chain holds: nothing is kept that was never deduped, and every finding
192
- # cites a line its hunk carries.
191
+ # set derived from them, the critic's verdicts, and the subset critic-apply kept.
192
+ # Generated together so the chain holds: nothing is kept that was never deduped,
193
+ # every finding cites a line its hunk carries, and every evidence snippet is the
194
+ # worktree line it points at.
193
195
  python3 - "$RUN_DIR" <<'FINDINGS'
194
196
  import json, pathlib, sys
195
197
 
@@ -202,9 +204,9 @@ FINDINGS = [
202
204
  'domain': 'security',
203
205
  'file': 'src/settings/cache.ts',
204
206
  'line': 4,
205
- 'severity': 'high',
207
+ 'evidence': 'store.set(tenantId, settings)',
206
208
  'risk': {'impact': 'high', 'likelihood': 'likely', 'confidence': 'high', 'action': 'must-fix'},
207
- 'score': 8,
209
+ 'score': 8.8,
208
210
  'title': 'Tenant settings cache is a process-global Map with no eviction',
209
211
  'description': """Observation: put() writes into a module-level Map keyed by tenant id (src/settings/cache.ts:4), with no size bound and no TTL.
210
212
 
@@ -218,9 +220,9 @@ Suggested direction: bound the map and give entries a TTL, or key the cache per
218
220
  'domain': 'bugs',
219
221
  'file': 'src/settings/loader.ts',
220
222
  'line': 12,
221
- 'severity': 'medium',
223
+ 'evidence': 'const fresh = await fetchSettings(tenantId)',
222
224
  'risk': {'impact': 'medium', 'likelihood': 'possible', 'confidence': 'medium', 'action': 'should-fix'},
223
- 'score': 6,
225
+ 'score': 5.1,
224
226
  'title': 'Concurrent loads for the same tenant each hit the network',
225
227
  'description': """Observation: load() checks the cache, then awaits fetchSettings before writing back (src/settings/loader.ts:12).
226
228
 
@@ -234,9 +236,9 @@ Suggested direction: cache the in-flight promise rather than the resolved value.
234
236
  'domain': 'architecture',
235
237
  'file': 'src/settings/loader.ts',
236
238
  'line': 10,
237
- 'severity': 'medium',
239
+ 'evidence': 'const hit = get(tenantId)',
238
240
  'risk': {'impact': 'medium', 'likelihood': 'possible', 'confidence': 'medium', 'action': 'consider'},
239
- 'score': 5,
241
+ 'score': 4.7,
240
242
  'title': 'The loader owns the cache rather than being handed one',
241
243
  'description': """Observation: load() calls the cache module's free functions directly (src/settings/loader.ts:10).
242
244
 
@@ -250,9 +252,9 @@ Suggested direction: take the cache as a parameter.""",
250
252
  'domain': 'performance',
251
253
  'file': 'src/settings/cache.ts',
252
254
  'line': 12,
253
- 'severity': 'low',
255
+ 'evidence': 'store.clear()',
254
256
  'risk': {'impact': 'low', 'likelihood': 'possible', 'confidence': 'medium', 'action': 'consider'},
255
- 'score': 3,
257
+ 'score': 3.5,
256
258
  'title': 'clear() evicts every tenant, not the one whose settings changed',
257
259
  'description': """Observation: clear() calls store.clear() (src/settings/cache.ts:12) and is the only invalidation the module offers.
258
260
 
@@ -266,9 +268,9 @@ Suggested direction: add delete(tenantId) and leave clear() for shutdown.""",
266
268
  'domain': 'code-smells',
267
269
  'file': 'src/settings/cache.ts',
268
270
  'line': 8,
269
- 'severity': 'low',
270
- 'risk': {'impact': 'low', 'likelihood': 'unlikely', 'confidence': 'medium', 'action': 'optional'},
271
- 'score': 2,
271
+ 'evidence': 'return store.get(tenantId)',
272
+ 'risk': {'impact': 'low', 'likelihood': 'edge-case', 'confidence': 'medium', 'action': 'optional'},
273
+ 'score': 2.3,
272
274
  'title': 'get() hands back the stored object, so a caller can mutate the cache',
273
275
  'description': """Observation: get() returns store.get(tenantId) directly (src/settings/cache.ts:8).
274
276
 
@@ -278,12 +280,22 @@ Suggested direction: freeze the value on put, or return a copy.""",
278
280
  },
279
281
  ]
280
282
 
281
- # The critic kept the three above its bar and dropped the two below it.
283
+ # smell-1 scored under the default threshold of 3, so dedupe set it aside and
284
+ # the critic never saw it. Of the four it did see, it kept three and dropped one.
285
+ BELOW_THRESHOLD = {'smell-1'}
282
286
  KEPT = {'security-1', 'bugs-1', 'arch-1'}
283
287
 
288
+ # Severity is derived from risk.impact; specialists do not write it.
289
+ SEVERITY = {'critical': 'blocker', 'high': 'high', 'medium': 'medium', 'low': 'low'}
290
+
284
291
  def without(finding, *keys):
285
292
  return {k: v for k, v in finding.items() if k not in keys}
286
293
 
294
+ def derived(finding):
295
+ return {**finding, 'severity': SEVERITY[finding['risk']['impact']]}
296
+
297
+ deduped = [derived(without(f, 'focus')) for f in FINDINGS if f['id'] not in BELOW_THRESHOLD]
298
+
287
299
  findings_dir = run / 'findings'
288
300
  findings_dir.mkdir(parents=True, exist_ok=True)
289
301
  for focus in ('security', 'bugs', 'performance', 'code-smells', 'architecture'):
@@ -291,11 +303,38 @@ for focus in ('security', 'bugs', 'performance', 'code-smells', 'architecture'):
291
303
  (findings_dir / f'{focus}.json').write_text(json.dumps(mine, indent=2) + '\n')
292
304
  (findings_dir / 'tests.json').write_text('[]\n')
293
305
 
294
- (run / 'findings.deduped.json').write_text(
295
- json.dumps([without(f, 'focus') for f in FINDINGS], indent=2) + '\n'
306
+ (run / 'findings.deduped.json').write_text(json.dumps(deduped, indent=2) + '\n')
307
+ (run / 'threshold-dropped.json').write_text(
308
+ json.dumps(
309
+ [{'id': f['id'], 'score': f['score'], 'title': f['title']} for f in FINDINGS if f['id'] in BELOW_THRESHOLD],
310
+ indent=2,
311
+ ) + '\n'
312
+ )
313
+ # Same file within 8 lines, more than one domain: what dedupe groups for the critic.
314
+ (run / 'merge-candidates.json').write_text(
315
+ json.dumps([['security-1', 'perf-1'], ['arch-1', 'bugs-1']], indent=2) + '\n'
316
+ )
317
+
318
+ CHECKED = {
319
+ 'security-1': ['src/settings/cache.ts:1-9', 'src/settings/loader.ts:9-15'],
320
+ 'bugs-1': ['src/settings/loader.ts:9-15'],
321
+ 'arch-1': ['src/settings/loader.ts:1-15'],
322
+ 'perf-1': ['src/settings/cache.ts:11-13'],
323
+ }
324
+ verdicts = []
325
+ for f in deduped:
326
+ if f['id'] in KEPT:
327
+ verdicts.append({'id': f['id'], 'verdict': 'keep', 'reason': 'confirmed in the worktree',
328
+ 'risk': f['risk'], 'checked': CHECKED[f['id']]})
329
+ else:
330
+ verdicts.append({'id': f['id'], 'verdict': 'drop', 'reason': 'one flush per settings change is not a measurable cost',
331
+ 'checked': CHECKED[f['id']]})
332
+ (run / 'critic.json').write_text(json.dumps(verdicts, indent=2) + '\n')
333
+ (run / 'critic-dropped.json').write_text(
334
+ json.dumps([{'id': v['id'], 'reason': v['reason']} for v in verdicts if v['verdict'] == 'drop'], indent=2) + '\n'
296
335
  )
297
336
  (run / 'findings.kept.json').write_text(
298
- json.dumps([without(f, 'focus', 'score') for f in FINDINGS if f['id'] in KEPT], indent=2) + '\n'
337
+ json.dumps([f for f in deduped if f['id'] in KEPT], indent=2) + '\n'
299
338
  )
300
339
  FINDINGS
301
340
 
@@ -303,8 +342,10 @@ echo "http://127.0.0.1:4599" > "$RUN_DIR/state/server-info"
303
342
 
304
343
  cat > "$RUN_DIR/brief.json" <<'JSON'
305
344
  {
306
- "summary": "Adds a process-global cache in front of tenant settings loads.",
307
- "riskAreas": ["tenant isolation", "cache invalidation"],
308
- "conventions": []
345
+ "purpose": "Adds a process-global cache in front of tenant settings loads.",
346
+ "changes": ["cache module gains put and get", "loader reads through the cache"],
347
+ "watchItems": [],
348
+ "unclear": ["how a settings change is meant to invalidate the cache"],
349
+ "reviewRules": []
309
350
  }
310
351
  JSON
@@ -142,7 +142,8 @@ cat > "$RUN_DIR/log.jsonl" <<'LOG'
142
142
  {"stage":"context","status":"done"}
143
143
  {"stage":"specialists","status":"done"}
144
144
  {"stage":"dedupe","status":"done"}
145
- {"stage":"critic","status":"done"}
145
+ {"stage":"critic","status":"running"}
146
+ {"stage":"critic","status":"done","kept":5,"dropped":0,"merged":0,"capped":0}
146
147
  {"stage":"peer-review","status":"done","provider":"claude"}
147
148
  {"stage":"report","status":"done"}
148
149
  LOG
@@ -225,6 +226,7 @@ cat > "$RUN_DIR/findings.final.json" <<'JSON'
225
226
  "id": "security-1",
226
227
  "file": "src/settings/cache.ts",
227
228
  "line": 4,
229
+ "evidence": "store.set(tenantId, settings)",
228
230
  "severity": "high",
229
231
  "risk": { "impact": "high", "likelihood": "likely", "confidence": "high", "action": "must-fix" },
230
232
  "domain": "security",
@@ -235,6 +237,7 @@ cat > "$RUN_DIR/findings.final.json" <<'JSON'
235
237
  "id": "bugs-1",
236
238
  "file": "src/settings/loader.ts",
237
239
  "line": 12,
240
+ "evidence": "const fresh = await fetchSettings(tenantId)",
238
241
  "severity": "medium",
239
242
  "risk": { "impact": "medium", "likelihood": "possible", "confidence": "medium", "action": "should-fix" },
240
243
  "domain": "bugs",
@@ -245,6 +248,7 @@ cat > "$RUN_DIR/findings.final.json" <<'JSON'
245
248
  "id": "perf-1",
246
249
  "file": "src/settings/cache.ts",
247
250
  "line": 12,
251
+ "evidence": "store.clear()",
248
252
  "severity": "low",
249
253
  "risk": { "impact": "low", "likelihood": "possible", "confidence": "medium", "action": "consider" },
250
254
  "domain": "performance",
@@ -255,6 +259,7 @@ cat > "$RUN_DIR/findings.final.json" <<'JSON'
255
259
  "id": "arch-1",
256
260
  "file": "src/settings/loader.ts",
257
261
  "line": 10,
262
+ "evidence": "const hit = get(tenantId)",
258
263
  "severity": "medium",
259
264
  "risk": { "impact": "medium", "likelihood": "possible", "confidence": "medium", "action": "consider" },
260
265
  "domain": "architecture",
@@ -265,8 +270,9 @@ cat > "$RUN_DIR/findings.final.json" <<'JSON'
265
270
  "id": "smell-1",
266
271
  "file": "src/settings/cache.ts",
267
272
  "line": 8,
273
+ "evidence": "return store.get(tenantId)",
268
274
  "severity": "low",
269
- "risk": { "impact": "low", "likelihood": "unlikely", "confidence": "medium", "action": "optional" },
275
+ "risk": { "impact": "low", "likelihood": "edge-case", "confidence": "medium", "action": "optional" },
270
276
  "domain": "code-smells",
271
277
  "title": "get() hands back the stored object, so a caller can mutate the cache",
272
278
  "description": "Observation: get() returns store.get(tenantId) directly (src/settings/cache.ts:8).\n\nWhy it matters: a caller that edits the returned settings edits every later reader's copy.\n\nSuggested direction: freeze the value on put, or return a copy."