@1aboveio/skills 0.0.0-stage → 1.0.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 (633) hide show
  1. package/README.md +30 -2
  2. package/bin/cli.mjs +14 -0
  3. package/bin/select-core.mjs +172 -0
  4. package/package.json +146 -4
  5. package/skills/app-dev/airflow/LICENSE +3 -0
  6. package/skills/app-dev/airflow/SKILL.md +199 -0
  7. package/skills/app-dev/airflow/release-check.mjs +93 -0
  8. package/skills/app-dev/android-foundation/LICENSE +3 -0
  9. package/skills/app-dev/android-foundation/SKILL.md +192 -0
  10. package/skills/app-dev/android-foundation/assets/android-ci-job.yml +217 -0
  11. package/skills/app-dev/android-foundation/references/ci.md +87 -0
  12. package/skills/app-dev/android-foundation/references/gotchas.md +221 -0
  13. package/skills/app-dev/android-foundation/references/setup.md +164 -0
  14. package/skills/app-dev/android-foundation/release-check.mjs +93 -0
  15. package/skills/app-dev/android-foundation/scripts/device-run.sh +176 -0
  16. package/skills/app-dev/android-foundation/scripts/device-run.test.mjs +172 -0
  17. package/skills/app-dev/app-debug/LICENSE +3 -0
  18. package/skills/app-dev/app-debug/SKILL.md +110 -0
  19. package/skills/app-dev/app-debug/references/common-errors.md +128 -0
  20. package/skills/app-dev/app-debug/release-check.mjs +93 -0
  21. package/skills/app-dev/better-auth/LICENSE +3 -0
  22. package/skills/app-dev/better-auth/SKILL.md +598 -0
  23. package/skills/app-dev/better-auth/references/feishu-api.md +270 -0
  24. package/skills/app-dev/better-auth/release-check.mjs +93 -0
  25. package/skills/app-dev/data-catalog/LICENSE +3 -0
  26. package/skills/app-dev/data-catalog/SKILL.md +204 -0
  27. package/skills/app-dev/data-catalog/agents/openai.yaml +3 -0
  28. package/skills/app-dev/data-catalog/references/data-catalog-conventions.md +75 -0
  29. package/skills/app-dev/data-catalog/references/field-definitions.md +116 -0
  30. package/skills/app-dev/data-catalog/references/format.md +121 -0
  31. package/skills/app-dev/data-catalog/release-check.mjs +93 -0
  32. package/skills/app-dev/data-catalog/scripts/data-catalog.test.mjs +36 -0
  33. package/skills/app-dev/data-catalog/scripts/data_catalog.py +1392 -0
  34. package/skills/app-dev/data-catalog/scripts/persist-conventions-core.mjs +117 -0
  35. package/skills/app-dev/data-catalog/scripts/persist-conventions.mjs +17 -0
  36. package/skills/app-dev/data-catalog/scripts/test_data_catalog.py +571 -0
  37. package/skills/app-dev/ios-foundation/LICENSE +3 -0
  38. package/skills/app-dev/ios-foundation/SKILL.md +184 -0
  39. package/skills/app-dev/ios-foundation/references/setup.md +243 -0
  40. package/skills/app-dev/ios-foundation/release-check.mjs +93 -0
  41. package/skills/app-dev/monorepo/LICENSE +3 -0
  42. package/skills/app-dev/monorepo/SKILL.md +462 -0
  43. package/skills/app-dev/monorepo/release-check.mjs +93 -0
  44. package/skills/app-dev/nextjs-fullstack/LICENSE +3 -0
  45. package/skills/app-dev/nextjs-fullstack/SKILL.md +211 -0
  46. package/skills/app-dev/nextjs-fullstack/conventions.md +318 -0
  47. package/skills/app-dev/nextjs-fullstack/frontend-conventions.md +61 -0
  48. package/skills/app-dev/nextjs-fullstack/nextjs16.md +287 -0
  49. package/skills/app-dev/nextjs-fullstack/release-check.mjs +93 -0
  50. package/skills/app-dev/nextjs-fullstack/server-actions.md +409 -0
  51. package/skills/app-dev/prisma-setup/LICENSE +3 -0
  52. package/skills/app-dev/prisma-setup/SKILL.md +181 -0
  53. package/skills/app-dev/prisma-setup/nextjs.md +258 -0
  54. package/skills/app-dev/prisma-setup/release-check.mjs +93 -0
  55. package/skills/app-dev/prisma-setup/turborepo.md +301 -0
  56. package/skills/app-dev/pyspark/LICENSE +3 -0
  57. package/skills/app-dev/pyspark/SKILL.md +127 -0
  58. package/skills/app-dev/pyspark/assets/templates/etl.py +748 -0
  59. package/skills/app-dev/pyspark/assets/templates/utils/__init__.py +1 -0
  60. package/skills/app-dev/pyspark/assets/templates/utils/hudi_metadata.py +14 -0
  61. package/skills/app-dev/pyspark/references/diagnosis-and-profiling.md +169 -0
  62. package/skills/app-dev/pyspark/references/etl-contract.md +129 -0
  63. package/skills/app-dev/pyspark/references/production-validation.md +131 -0
  64. package/skills/app-dev/pyspark/references/reconciliation.md +38 -0
  65. package/skills/app-dev/pyspark/references/transformation-design.md +190 -0
  66. package/skills/app-dev/pyspark/references/velocity-feature-calculation.md +193 -0
  67. package/skills/app-dev/pyspark/release-check.mjs +93 -0
  68. package/skills/app-dev/pyspark/scripts/spark_eventlog_summary.py +223 -0
  69. package/skills/app-dev/python-backend/LICENSE +3 -0
  70. package/skills/app-dev/python-backend/SKILL.md +325 -0
  71. package/skills/app-dev/python-backend/assets/uv-install.sh +48 -0
  72. package/skills/app-dev/python-backend/references/uv-installs.md +83 -0
  73. package/skills/app-dev/python-backend/release-check.mjs +93 -0
  74. package/skills/app-dev/shadcn/LICENSE +3 -0
  75. package/skills/app-dev/shadcn/SKILL.md +120 -0
  76. package/skills/app-dev/shadcn/assets/shadcn-small.png +0 -0
  77. package/skills/app-dev/shadcn/assets/shadcn.png +0 -0
  78. package/skills/app-dev/shadcn/cli.md +411 -0
  79. package/skills/app-dev/shadcn/customization.md +224 -0
  80. package/skills/app-dev/shadcn/mcp.md +101 -0
  81. package/skills/app-dev/shadcn/release-check.mjs +93 -0
  82. package/skills/app-dev/shadcn/rules/base-vs-radix.md +323 -0
  83. package/skills/app-dev/shadcn/rules/component-selection.md +67 -0
  84. package/skills/app-dev/shadcn/rules/composition.md +195 -0
  85. package/skills/app-dev/shadcn/rules/data-table.md +201 -0
  86. package/skills/app-dev/shadcn/rules/forms.md +255 -0
  87. package/skills/app-dev/shadcn/rules/icons.md +103 -0
  88. package/skills/app-dev/shadcn/rules/styling.md +167 -0
  89. package/skills/app-dev/zod-v4/LICENSE +3 -0
  90. package/skills/app-dev/zod-v4/SKILL.md +286 -0
  91. package/skills/app-dev/zod-v4/release-check.mjs +93 -0
  92. package/skills/cicd/cloud-build/LICENSE +3 -0
  93. package/skills/cicd/cloud-build/SKILL.md +707 -0
  94. package/skills/cicd/cloud-build/release-check.mjs +93 -0
  95. package/skills/cicd/cloud-debug/LICENSE +3 -0
  96. package/skills/cicd/cloud-debug/SKILL.md +315 -0
  97. package/skills/cicd/cloud-debug/references/build-failures.md +238 -0
  98. package/skills/cicd/cloud-debug/references/deploy-failures.md +376 -0
  99. package/skills/cicd/cloud-debug/references/pipeline-failures.md +378 -0
  100. package/skills/cicd/cloud-debug/release-check.mjs +93 -0
  101. package/skills/cicd/cloud-deploy/LICENSE +3 -0
  102. package/skills/cicd/cloud-deploy/SKILL.md +230 -0
  103. package/skills/cicd/cloud-deploy/references/config-templates.md +257 -0
  104. package/skills/cicd/cloud-deploy/release-check.mjs +93 -0
  105. package/skills/cicd/docker/LICENSE +3 -0
  106. package/skills/cicd/docker/SKILL.md +151 -0
  107. package/skills/cicd/docker/release-check.mjs +93 -0
  108. package/skills/cicd/github-actions/LICENSE +3 -0
  109. package/skills/cicd/github-actions/SKILL.md +111 -0
  110. package/skills/cicd/github-actions/references/audit-profile-optimize.md +150 -0
  111. package/skills/cicd/github-actions/references/ci-case-studies.md +141 -0
  112. package/skills/cicd/github-actions/references/conditional-ci.md +214 -0
  113. package/skills/cicd/github-actions/references/debugging.md +13 -0
  114. package/skills/cicd/github-actions/references/pipeline.md +87 -0
  115. package/skills/cicd/github-actions/references/post-deploy-and-publishing.md +33 -0
  116. package/skills/cicd/github-actions/references/runners.md +253 -0
  117. package/skills/cicd/github-actions/references/security.md +29 -0
  118. package/skills/cicd/github-actions/references/speed-and-audit.md +115 -0
  119. package/skills/cicd/github-actions/references/two-pass-reading.md +92 -0
  120. package/skills/cicd/github-actions/release-check.mjs +93 -0
  121. package/skills/cicd/github-actions/scripts/ci-run-audit-core.mjs +612 -0
  122. package/skills/cicd/github-actions/scripts/ci-run-audit.mjs +4 -0
  123. package/skills/cicd/github-actions/scripts/ci-run-audit.test.mjs +637 -0
  124. package/skills/cicd/github-flow/LICENSE +3 -0
  125. package/skills/cicd/github-flow/SKILL.md +123 -0
  126. package/skills/cicd/github-flow/references/ci-conventions.md +14 -0
  127. package/skills/cicd/github-flow/release-check.mjs +93 -0
  128. package/skills/cicd/github-flow/scripts/flow-core.mjs +105 -0
  129. package/skills/cicd/github-flow/scripts/flow.mjs +81 -0
  130. package/skills/cicd/github-flow/scripts/flow.test.mjs +114 -0
  131. package/skills/cicd/github-flow/scripts/persist-conventions-core.mjs +117 -0
  132. package/skills/cicd/github-flow/scripts/persist-conventions.mjs +49 -0
  133. package/skills/cicd/github-flow/scripts/persist-conventions.test.mjs +108 -0
  134. package/skills/cicd/google-cloud/LICENSE +3 -0
  135. package/skills/cicd/google-cloud/SKILL.md +119 -0
  136. package/skills/cicd/google-cloud/references/gcs.md +469 -0
  137. package/skills/cicd/google-cloud/references/iam.md +451 -0
  138. package/skills/cicd/google-cloud/references/project.md +349 -0
  139. package/skills/cicd/google-cloud/references/secrets.md +336 -0
  140. package/skills/cicd/google-cloud/references/vpc.md +312 -0
  141. package/skills/cicd/google-cloud/release-check.mjs +93 -0
  142. package/skills/cicd/google-cloud/scripts/create-sa.sh +36 -0
  143. package/skills/cicd/google-cloud/scripts/gcp-config.sh +31 -0
  144. package/skills/cicd/google-cloud/scripts/grant-iap.sh +41 -0
  145. package/skills/cicd/google-cloud/scripts/setup-secrets.sh +48 -0
  146. package/skills/cicd/mergify/LICENSE +3 -0
  147. package/skills/cicd/mergify/SKILL.md +102 -0
  148. package/skills/cicd/mergify/assets/templates/mergify.yml +215 -0
  149. package/skills/cicd/mergify/assets/templates/ruleset.json +46 -0
  150. package/skills/cicd/mergify/references/branch-protection.md +282 -0
  151. package/skills/cicd/mergify/references/configuration.md +274 -0
  152. package/skills/cicd/mergify/references/diagnosis.md +109 -0
  153. package/skills/cicd/mergify/references/stacks.md +84 -0
  154. package/skills/cicd/mergify/references/traps.md +119 -0
  155. package/skills/cicd/mergify/references/watch-contract.md +274 -0
  156. package/skills/cicd/mergify/release-check.mjs +93 -0
  157. package/skills/cicd/mergify/scripts/audit-core.mjs +208 -0
  158. package/skills/cicd/mergify/scripts/audit.mjs +4 -0
  159. package/skills/cicd/mergify/scripts/audit.test.mjs +201 -0
  160. package/skills/cicd/mergify/scripts/entrypoints.test.mjs +106 -0
  161. package/skills/cicd/mergify/scripts/pr-620-mergify-comments.fixture.json +30 -0
  162. package/skills/cicd/mergify/scripts/stack-push-core.mjs +258 -0
  163. package/skills/cicd/mergify/scripts/stack-push.mjs +4 -0
  164. package/skills/cicd/mergify/scripts/stack-push.test.mjs +233 -0
  165. package/skills/cicd/mergify/scripts/watch-pr-delivery-core.mjs +1065 -0
  166. package/skills/cicd/mergify/scripts/watch-pr-delivery.mjs +4 -0
  167. package/skills/cicd/mergify/scripts/watch-pr-delivery.test.mjs +1826 -0
  168. package/skills/cicd/podman/LICENSE +3 -0
  169. package/skills/cicd/podman/SKILL.md +71 -0
  170. package/skills/cicd/podman/agents/openai.yaml +4 -0
  171. package/skills/cicd/podman/assets/templates/podman-compose-socket-directory.yml +6 -0
  172. package/skills/cicd/podman/assets/templates/podman-service-override.conf +3 -0
  173. package/skills/cicd/podman/references/compose-compatibility.md +70 -0
  174. package/skills/cicd/podman/references/networking-and-ports.md +74 -0
  175. package/skills/cicd/podman/references/rootless-services-and-sockets.md +156 -0
  176. package/skills/cicd/podman/references/troubleshooting.md +98 -0
  177. package/skills/cicd/podman/release-check.mjs +93 -0
  178. package/skills/content/article-review/LICENSE +3 -0
  179. package/skills/content/article-review/SKILL.md +66 -0
  180. package/skills/content/article-review/references/report-format.md +68 -0
  181. package/skills/content/article-review/references/template-guide.md +79 -0
  182. package/skills/content/article-review/references/template-wechat-payments.md +125 -0
  183. package/skills/content/article-review/release-check.mjs +93 -0
  184. package/skills/content/article-to-image/LICENSE +3 -0
  185. package/skills/content/article-to-image/SKILL.md +91 -0
  186. package/skills/content/article-to-image/references/formats.md +53 -0
  187. package/skills/content/article-to-image/release-check.mjs +93 -0
  188. package/skills/content/flowchart-svg/LICENSE +3 -0
  189. package/skills/content/flowchart-svg/SKILL.md +43 -0
  190. package/skills/content/flowchart-svg/release-check.mjs +93 -0
  191. package/skills/content/flowchart-svg/scripts/export_png.sh +8 -0
  192. package/skills/content/image-gen/LICENSE +3 -0
  193. package/skills/content/image-gen/SKILL.md +192 -0
  194. package/skills/content/image-gen/release-check.mjs +93 -0
  195. package/skills/content/image-gen/scripts/credentials.mjs +217 -0
  196. package/skills/content/image-gen/scripts/credentials.test.mjs +212 -0
  197. package/skills/content/image-gen/scripts/generate.mjs +240 -0
  198. package/skills/content/image-gen/scripts/options.mjs +293 -0
  199. package/skills/content/image-gen/scripts/options.test.mjs +245 -0
  200. package/skills/content/mermaid/LICENSE +3 -0
  201. package/skills/content/mermaid/SKILL.md +635 -0
  202. package/skills/content/mermaid/release-check.mjs +93 -0
  203. package/skills/content/wechat-article-writer/LICENSE +3 -0
  204. package/skills/content/wechat-article-writer/SKILL.md +92 -0
  205. package/skills/content/wechat-article-writer/references/style-selector.md +133 -0
  206. package/skills/content/wechat-article-writer/references/writing-rules.md +152 -0
  207. package/skills/content/wechat-article-writer/release-check.mjs +93 -0
  208. package/skills/content/wechat-article-writer/templates/wechat-comparison.md +52 -0
  209. package/skills/content/wechat-article-writer/templates/wechat-howto.md +51 -0
  210. package/skills/content/wechat-article-writer/templates/wechat-insight.md +50 -0
  211. package/skills/content/wechat-article-writer/templates/wechat-pitfall.md +52 -0
  212. package/skills/content/wechat-publisher/LICENSE +3 -0
  213. package/skills/content/wechat-publisher/SKILL.md +171 -0
  214. package/skills/content/wechat-publisher/references/api.md +105 -0
  215. package/skills/content/wechat-publisher/references/layout-rules.md +57 -0
  216. package/skills/content/wechat-publisher/references/wechat-safe-template.html +86 -0
  217. package/skills/content/wechat-publisher/release-check.mjs +93 -0
  218. package/skills/content/wechat-publisher/scripts/md_to_wechat_html.py +194 -0
  219. package/skills/content/wechat-publisher/scripts/publish.py +239 -0
  220. package/skills/content/wechat-search/LICENSE +3 -0
  221. package/skills/content/wechat-search/SKILL.md +211 -0
  222. package/skills/content/wechat-search/release-check.mjs +93 -0
  223. package/skills/content/wechat-search/scripts/start-chrome.sh +83 -0
  224. package/skills/content/xai-search/LICENSE +3 -0
  225. package/skills/content/xai-search/SKILL.md +79 -0
  226. package/skills/content/xai-search/release-check.mjs +93 -0
  227. package/skills/content/xai-search/scripts/xai_search.sh +187 -0
  228. package/skills/content/xai-search/tests/smoke_test.sh +36 -0
  229. package/skills/core/distill-writing/LICENSE +3 -0
  230. package/skills/core/distill-writing/SKILL.md +74 -0
  231. package/skills/core/distill-writing/references/extraction.md +95 -0
  232. package/skills/core/distill-writing/references/updating.md +46 -0
  233. package/skills/core/distill-writing/release-check.mjs +93 -0
  234. package/skills/core/doc-to-md/LICENSE +3 -0
  235. package/skills/core/doc-to-md/SKILL.md +78 -0
  236. package/skills/core/doc-to-md/release-check.mjs +93 -0
  237. package/skills/core/doc-to-md/scripts/convert.py +157 -0
  238. package/skills/core/editorial-review/LICENSE +3 -0
  239. package/skills/core/editorial-review/SKILL.md +79 -0
  240. package/skills/core/editorial-review/references/checklist.md +94 -0
  241. package/skills/core/editorial-review/release-check.mjs +93 -0
  242. package/skills/core/house-style/LICENSE +3 -0
  243. package/skills/core/house-style/SKILL.md +33 -0
  244. package/skills/core/house-style/references/defaults.md +66 -0
  245. package/skills/core/house-style/references/rules.md +267 -0
  246. package/skills/core/house-style/references/sources.md +27 -0
  247. package/skills/core/house-style/references/style-en.md +131 -0
  248. package/skills/core/house-style/references/style-zh.md +141 -0
  249. package/skills/core/house-style/references/terminology-zh.md +223 -0
  250. package/skills/core/house-style/release-check.mjs +93 -0
  251. package/skills/core/house-style/templates/brd.md +103 -0
  252. package/skills/core/house-style/templates/commercial-proposal.md +93 -0
  253. package/skills/core/house-style/templates/compliance-policy.md +109 -0
  254. package/skills/core/house-style/templates/gap-analysis.md +105 -0
  255. package/skills/core/house-style/templates/general.md +63 -0
  256. package/skills/core/house-style/templates/integration-guide.md +98 -0
  257. package/skills/core/house-style/templates/prd.md +81 -0
  258. package/skills/core/house-style/templates/risk-operations-procedure.md +94 -0
  259. package/skills/core/house-style/templates/technical-spec.md +96 -0
  260. package/skills/core/polish-cn/LICENSE +3 -0
  261. package/skills/core/polish-cn/SKILL.md +102 -0
  262. package/skills/core/polish-cn/references/brief.md +93 -0
  263. package/skills/core/polish-cn/references/models.md +76 -0
  264. package/skills/core/polish-cn/release-check.mjs +93 -0
  265. package/skills/core/polish-cn/scripts/polish-cn.test.mjs +244 -0
  266. package/skills/core/polish-cn/scripts/polish_blocks.py +489 -0
  267. package/skills/core/reg-parser/LICENSE +3 -0
  268. package/skills/core/reg-parser/SKILL.md +212 -0
  269. package/skills/core/reg-parser/references/checklist-schema.md +223 -0
  270. package/skills/core/reg-parser/references/checklists/hk-amlo-checklist.json +1306 -0
  271. package/skills/core/reg-parser/references/checklists/hk-amlo-checklist.md +469 -0
  272. package/skills/core/reg-parser/references/checklists/hk-fi-corporate-onboarding-cdd-checklist.json +634 -0
  273. package/skills/core/reg-parser/references/checklists/hk-fi-corporate-onboarding-cdd-checklist.md +233 -0
  274. package/skills/core/reg-parser/references/checklists/hk-mso-guideline-checklist.json +2194 -0
  275. package/skills/core/reg-parser/references/checklists/hk-mso-guideline-checklist.md +771 -0
  276. package/skills/core/reg-parser/references/checklists/kyc-aml-checklist.md +258 -0
  277. package/skills/core/reg-parser/references/checklists/visa-vars-checklist.md +1058 -0
  278. package/skills/core/reg-parser/references/mandates/hk-amlo-cap615.md +389 -0
  279. package/skills/core/reg-parser/references/mandates/visa-vars.md +205 -0
  280. package/skills/core/reg-parser/release-check.mjs +93 -0
  281. package/skills/core/reg-parser/scripts/compare_checklist.py +230 -0
  282. package/skills/core/reg-parser/scripts/dump_checklist.py +361 -0
  283. package/skills/core/reg-parser/scripts/preprocess-mandate.sh +187 -0
  284. package/skills/core/reg-parser/scripts/reg-parser.test.mjs +165 -0
  285. package/skills/core/reg-parser/scripts/test_quarantine.sh +105 -0
  286. package/skills/core/reg-parser/scripts/validate_checklist.py +563 -0
  287. package/skills/core/reg-parser/scripts/validate_mandate.py +273 -0
  288. package/skills/core/terminology/LICENSE +3 -0
  289. package/skills/core/terminology/SKILL.md +62 -0
  290. package/skills/core/terminology/references/format.md +50 -0
  291. package/skills/core/terminology/references/init.md +32 -0
  292. package/skills/core/terminology/release-check.mjs +93 -0
  293. package/skills/core/write-doc/LICENSE +3 -0
  294. package/skills/core/write-doc/SKILL.md +141 -0
  295. package/skills/core/write-doc/references/bilingual.md +60 -0
  296. package/skills/core/write-doc/release-check.mjs +93 -0
  297. package/skills/engineering/better-review/LICENSE +3 -0
  298. package/skills/engineering/better-review/SKILL.md +249 -0
  299. package/skills/engineering/better-review/agents/openai.yaml +9 -0
  300. package/skills/engineering/better-review/references/domain-hazards.md +53 -0
  301. package/skills/engineering/better-review/references/passes.md +147 -0
  302. package/skills/engineering/better-review/references/result.md +151 -0
  303. package/skills/engineering/better-review/release-check.mjs +93 -0
  304. package/skills/engineering/better-review/scripts/pr-context.mjs +212 -0
  305. package/skills/engineering/better-review/scripts/pr-context.test.mjs +168 -0
  306. package/skills/engineering/better-review/scripts/rereview-scope.mjs +503 -0
  307. package/skills/engineering/better-review/scripts/rereview-scope.test.mjs +587 -0
  308. package/skills/engineering/better-review/scripts/scan-diff.mjs +365 -0
  309. package/skills/engineering/better-review/scripts/scan-diff.test.mjs +414 -0
  310. package/skills/engineering/better-review/scripts/vendor-sync.test.mjs +96 -0
  311. package/skills/engineering/e2e-test/LICENSE +3 -0
  312. package/skills/engineering/e2e-test/SKILL.md +98 -0
  313. package/skills/engineering/e2e-test/assets/ci-gates.cloudbuild.yaml +24 -0
  314. package/skills/engineering/e2e-test/references/authoring/auth-flows.md +159 -0
  315. package/skills/engineering/e2e-test/references/authoring/playwright-config.md +73 -0
  316. package/skills/engineering/e2e-test/references/authoring/playwright-patterns.md +144 -0
  317. package/skills/engineering/e2e-test/references/ci-integration.md +25 -0
  318. package/skills/engineering/e2e-test/references/ci-playwright-container.md +290 -0
  319. package/skills/engineering/e2e-test/references/presentation-sweep.md +104 -0
  320. package/skills/engineering/e2e-test/references/reviewing.md +41 -0
  321. package/skills/engineering/e2e-test/references/route-manifest.md +51 -0
  322. package/skills/engineering/e2e-test/release-check.mjs +93 -0
  323. package/skills/engineering/e2e-test/scripts/detect-routes-nextjs.mjs +200 -0
  324. package/skills/engineering/e2e-test/scripts/presentation-checks.mjs +171 -0
  325. package/skills/engineering/e2e-test/scripts/presentation-checks.test.mjs +106 -0
  326. package/skills/engineering/e2e-test/scripts/presentation-perceivability.mjs +179 -0
  327. package/skills/engineering/e2e-test/scripts/presentation-perceivability.test.mjs +117 -0
  328. package/skills/engineering/e2e-test/scripts/presentation-reachability.mjs +154 -0
  329. package/skills/engineering/e2e-test/scripts/presentation-reachability.test.mjs +69 -0
  330. package/skills/engineering/e2e-test/scripts/presentation-render-health.mjs +141 -0
  331. package/skills/engineering/e2e-test/scripts/presentation-render-health.test.mjs +98 -0
  332. package/skills/engineering/e2e-test/scripts/presentation-sweep.mjs +147 -0
  333. package/skills/engineering/e2e-test/scripts/presentation-sweep.test.mjs +71 -0
  334. package/skills/engineering/e2e-test/scripts/presentation-temporal.mjs +127 -0
  335. package/skills/engineering/e2e-test/scripts/presentation-temporal.test.mjs +59 -0
  336. package/skills/engineering/e2e-test/scripts/project-route-manifest.mjs +82 -0
  337. package/skills/engineering/e2e-test/scripts/project-route-manifest.test.mjs +76 -0
  338. package/skills/engineering/e2e-test/scripts/validate-manifest.mjs +106 -0
  339. package/skills/engineering/engineering-runtime/LICENSE +3 -0
  340. package/skills/engineering/engineering-runtime/SKILL.md +11 -0
  341. package/skills/engineering/engineering-runtime/agents/openai.yaml +6 -0
  342. package/skills/engineering/engineering-runtime/coherence/workflow.json +597 -0
  343. package/skills/engineering/engineering-runtime/engineering-runtime.test.mjs +68 -0
  344. package/skills/engineering/engineering-runtime/release-check.mjs +93 -0
  345. package/skills/engineering/engineering-runtime/scripts/exact-head-artifact.mjs +131 -0
  346. package/skills/engineering/engineering-runtime/scripts/head-check-set.mjs +398 -0
  347. package/skills/engineering/engineering-runtime/scripts/invocation-policy.mjs +187 -0
  348. package/skills/engineering/engineering-runtime/scripts/invocation-policy.test.mjs +95 -0
  349. package/skills/engineering/engineering-runtime/scripts/main-module.mjs +79 -0
  350. package/skills/engineering/engineering-runtime/scripts/main-module.test.mjs +288 -0
  351. package/skills/engineering/engineering-runtime/scripts/mergify-yaml.mjs +11 -0
  352. package/skills/engineering/engineering-runtime/scripts/package-lock.json +43 -0
  353. package/skills/engineering/engineering-runtime/scripts/package.json +10 -0
  354. package/skills/engineering/engineering-runtime/scripts/required-check-plan.mjs +223 -0
  355. package/skills/engineering/engineering-runtime/scripts/required-check-plan.test.mjs +96 -0
  356. package/skills/engineering/engineering-runtime/scripts/review-completion.mjs +56 -0
  357. package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +595 -0
  358. package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +189 -0
  359. package/skills/engineering/engineering-runtime/scripts/yaml-mapping.mjs +18 -0
  360. package/skills/engineering/ensure-coverage/LICENSE +3 -0
  361. package/skills/engineering/ensure-coverage/SKILL.md +129 -0
  362. package/skills/engineering/ensure-coverage/references/breadth/coverage-ledger.md +92 -0
  363. package/skills/engineering/ensure-coverage/references/breadth/inventory-contract.md +85 -0
  364. package/skills/engineering/ensure-coverage/references/breadth/surface-baseline.md +44 -0
  365. package/skills/engineering/ensure-coverage/references/breadth/surface-discovery.md +16 -0
  366. package/skills/engineering/ensure-coverage/references/depth/characterization.md +68 -0
  367. package/skills/engineering/ensure-coverage/references/depth/coverage.config.example.json +19 -0
  368. package/skills/engineering/ensure-coverage/references/depth/grading.md +35 -0
  369. package/skills/engineering/ensure-coverage/references/depth/mock-policy.md +57 -0
  370. package/skills/engineering/ensure-coverage/references/depth/test-smells.md +39 -0
  371. package/skills/engineering/ensure-coverage/references/enforcement/ci-contract.md +44 -0
  372. package/skills/engineering/ensure-coverage/references/examples/coverage-ledger.md +110 -0
  373. package/skills/engineering/ensure-coverage/references/examples/refund-flow.md +35 -0
  374. package/skills/engineering/ensure-coverage/references/presentation/axis.md +64 -0
  375. package/skills/engineering/ensure-coverage/references/process/audit-mode.md +27 -0
  376. package/skills/engineering/ensure-coverage/references/process/output-template.md +139 -0
  377. package/skills/engineering/ensure-coverage/references/process/test-strategy.md +56 -0
  378. package/skills/engineering/ensure-coverage/release-check.mjs +93 -0
  379. package/skills/engineering/ensure-coverage/schemas/coverage-config.schema.json +67 -0
  380. package/skills/engineering/ensure-coverage/schemas/coverage-file.schema.json +140 -0
  381. package/skills/engineering/ensure-coverage/scripts/README.md +99 -0
  382. package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.mjs +182 -0
  383. package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.test.mjs +96 -0
  384. package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.mjs +105 -0
  385. package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.test.mjs +47 -0
  386. package/skills/engineering/ensure-coverage/scripts/ci-audit.mjs +137 -0
  387. package/skills/engineering/ensure-coverage/scripts/ci-audit.test.mjs +86 -0
  388. package/skills/engineering/ensure-coverage/scripts/coverage-ledger.mjs +674 -0
  389. package/skills/engineering/ensure-coverage/scripts/coverage-ledger.test.mjs +447 -0
  390. package/skills/engineering/ensure-coverage/scripts/design-parity.mjs +591 -0
  391. package/skills/engineering/ensure-coverage/scripts/design-parity.test.mjs +552 -0
  392. package/skills/engineering/ensure-coverage/scripts/lint-tests.mjs +282 -0
  393. package/skills/engineering/ensure-coverage/scripts/lint-tests.test.mjs +289 -0
  394. package/skills/engineering/ensure-coverage/scripts/mock-policy-config.mjs +176 -0
  395. package/skills/engineering/ensure-coverage/scripts/mock-policy-config.test.mjs +119 -0
  396. package/skills/engineering/ensure-coverage/scripts/package.json +19 -0
  397. package/skills/engineering/golden-path/LICENSE +3 -0
  398. package/skills/engineering/golden-path/SKILL.md +230 -0
  399. package/skills/engineering/golden-path/agents/openai.yaml +3 -0
  400. package/skills/engineering/golden-path/assets/golden-paths.example.json +57 -0
  401. package/skills/engineering/golden-path/references/contract.md +239 -0
  402. package/skills/engineering/golden-path/references/migration.md +111 -0
  403. package/skills/engineering/golden-path/release-check.mjs +93 -0
  404. package/skills/engineering/golden-path/scripts/golden-path-core.mjs +1779 -0
  405. package/skills/engineering/golden-path/scripts/golden-path.mjs +6 -0
  406. package/skills/engineering/golden-path/scripts/golden-path.test.mjs +1457 -0
  407. package/skills/engineering/harness-runtime/LICENSE +3 -0
  408. package/skills/engineering/harness-runtime/SKILL.md +19 -0
  409. package/skills/engineering/harness-runtime/agents/openai.yaml +6 -0
  410. package/skills/engineering/harness-runtime/bin/discover-models.mjs +4 -0
  411. package/skills/engineering/harness-runtime/bin/model-catalog.mjs +4 -0
  412. package/skills/engineering/harness-runtime/contracts.md +15 -0
  413. package/skills/engineering/harness-runtime/discover-models.mjs +392 -0
  414. package/skills/engineering/harness-runtime/discover-models.test.mjs +363 -0
  415. package/skills/engineering/harness-runtime/fixtures/native-question-schemas.json +33 -0
  416. package/skills/engineering/harness-runtime/fixtures/question-responses.json +54 -0
  417. package/skills/engineering/harness-runtime/harness-runtime.test.mjs +967 -0
  418. package/skills/engineering/harness-runtime/index.mjs +767 -0
  419. package/skills/engineering/harness-runtime/model-catalog-seed.test.mjs +829 -0
  420. package/skills/engineering/harness-runtime/model-catalog.mjs +787 -0
  421. package/skills/engineering/harness-runtime/model-catalog.test.mjs +366 -0
  422. package/skills/engineering/harness-runtime/native-question-contracts.md +37 -0
  423. package/skills/engineering/harness-runtime/references/model-catalog-seed.json +408 -0
  424. package/skills/engineering/harness-runtime/references/model-catalog.md +57 -0
  425. package/skills/engineering/harness-runtime/release-check.mjs +93 -0
  426. package/skills/engineering/resolve-issues/LICENSE +3 -0
  427. package/skills/engineering/resolve-issues/SKILL.md +250 -0
  428. package/skills/engineering/resolve-issues/agents/openai.yaml +9 -0
  429. package/skills/engineering/resolve-issues/plugin-hooks.json +17 -0
  430. package/skills/engineering/resolve-issues/references/canary.md +28 -0
  431. package/skills/engineering/resolve-issues/references/ci.md +105 -0
  432. package/skills/engineering/resolve-issues/references/done.md +25 -0
  433. package/skills/engineering/resolve-issues/references/dropbox.md +90 -0
  434. package/skills/engineering/resolve-issues/references/expire.md +318 -0
  435. package/skills/engineering/resolve-issues/references/exploration.md +202 -0
  436. package/skills/engineering/resolve-issues/references/implementation.md +303 -0
  437. package/skills/engineering/resolve-issues/references/preflight.md +445 -0
  438. package/skills/engineering/resolve-issues/references/review.md +228 -0
  439. package/skills/engineering/resolve-issues/references/shared-modules.md +44 -0
  440. package/skills/engineering/resolve-issues/references/task-plan.md +264 -0
  441. package/skills/engineering/resolve-issues/release-check.mjs +93 -0
  442. package/skills/engineering/resolve-issues/scripts/cleanup-runs.mjs +364 -0
  443. package/skills/engineering/resolve-issues/scripts/cleanup-runs.test.mjs +280 -0
  444. package/skills/engineering/resolve-issues/scripts/discover-models.mjs +9 -0
  445. package/skills/engineering/resolve-issues/scripts/dropbox.mjs +473 -0
  446. package/skills/engineering/resolve-issues/scripts/dropbox.test.mjs +346 -0
  447. package/skills/engineering/resolve-issues/scripts/findings-reuse.mjs +98 -0
  448. package/skills/engineering/resolve-issues/scripts/findings-reuse.test.mjs +88 -0
  449. package/skills/engineering/resolve-issues/scripts/github-reviews.mjs +384 -0
  450. package/skills/engineering/resolve-issues/scripts/github-reviews.test.mjs +264 -0
  451. package/skills/engineering/resolve-issues/scripts/install-hooks.mjs +135 -0
  452. package/skills/engineering/resolve-issues/scripts/install-hooks.test.mjs +79 -0
  453. package/skills/engineering/resolve-issues/scripts/issue-tree.mjs +162 -0
  454. package/skills/engineering/resolve-issues/scripts/issue-tree.test.mjs +107 -0
  455. package/skills/engineering/resolve-issues/scripts/manual-invocation.test.mjs +14 -0
  456. package/skills/engineering/resolve-issues/scripts/model-catalog.mjs +9 -0
  457. package/skills/engineering/resolve-issues/scripts/next-actions-capacity.test.mjs +149 -0
  458. package/skills/engineering/resolve-issues/scripts/next-actions.mjs +856 -0
  459. package/skills/engineering/resolve-issues/scripts/next-actions.test.mjs +915 -0
  460. package/skills/engineering/resolve-issues/scripts/plan-sync.mjs +269 -0
  461. package/skills/engineering/resolve-issues/scripts/plan-sync.test.mjs +156 -0
  462. package/skills/engineering/resolve-issues/scripts/preflight-models.mjs +736 -0
  463. package/skills/engineering/resolve-issues/scripts/preflight-models.test.mjs +448 -0
  464. package/skills/engineering/resolve-issues/scripts/ready-check.mjs +185 -0
  465. package/skills/engineering/resolve-issues/scripts/ready-check.test.mjs +217 -0
  466. package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +98 -0
  467. package/skills/engineering/resolve-issues/scripts/rehydrate.test.mjs +91 -0
  468. package/skills/engineering/resolve-issues/scripts/review-verdict.mjs +509 -0
  469. package/skills/engineering/resolve-issues/scripts/review-verdict.test.mjs +1060 -0
  470. package/skills/engineering/resolve-issues/scripts/size-check.mjs +492 -0
  471. package/skills/engineering/resolve-issues/scripts/size-check.test.mjs +458 -0
  472. package/skills/engineering/resolve-release/LICENSE +3 -0
  473. package/skills/engineering/resolve-release/SKILL.md +215 -0
  474. package/skills/engineering/resolve-release/agents/openai.yaml +8 -0
  475. package/skills/engineering/resolve-release/flows/cloud-run-canary.md +13 -0
  476. package/skills/engineering/resolve-release/flows/cloud-run.md +7 -0
  477. package/skills/engineering/resolve-release/flows/root-tag-npm.md +13 -0
  478. package/skills/engineering/resolve-release/flows/workspace-npm.md +22 -0
  479. package/skills/engineering/resolve-release/references/canary.md +51 -0
  480. package/skills/engineering/resolve-release/references/candidate.md +41 -0
  481. package/skills/engineering/resolve-release/references/cut.md +131 -0
  482. package/skills/engineering/resolve-release/references/dark-launch.md +66 -0
  483. package/skills/engineering/resolve-release/references/flows.md +150 -0
  484. package/skills/engineering/resolve-release/references/preflight.md +154 -0
  485. package/skills/engineering/resolve-release/references/prepared-metadata.md +260 -0
  486. package/skills/engineering/resolve-release/references/promotion.md +82 -0
  487. package/skills/engineering/resolve-release/references/publish.md +232 -0
  488. package/skills/engineering/resolve-release/references/workspace-npm.md +103 -0
  489. package/skills/engineering/resolve-release/release-check.mjs +93 -0
  490. package/skills/engineering/resolve-release/scripts/apply.mjs +212 -0
  491. package/skills/engineering/resolve-release/scripts/apply.test.mjs +99 -0
  492. package/skills/engineering/resolve-release/scripts/dark-flags.mjs +134 -0
  493. package/skills/engineering/resolve-release/scripts/dark-flags.test.mjs +100 -0
  494. package/skills/engineering/resolve-release/scripts/flow.mjs +500 -0
  495. package/skills/engineering/resolve-release/scripts/flow.test.mjs +340 -0
  496. package/skills/engineering/resolve-release/scripts/green-head.mjs +231 -0
  497. package/skills/engineering/resolve-release/scripts/green-head.test.mjs +101 -0
  498. package/skills/engineering/resolve-release/scripts/npm-version-absence.mjs +138 -0
  499. package/skills/engineering/resolve-release/scripts/npm-version-absence.test.mjs +426 -0
  500. package/skills/engineering/resolve-release/scripts/plan-npm.test.mjs +659 -0
  501. package/skills/engineering/resolve-release/scripts/plan-toml-boundary.test.mjs +317 -0
  502. package/skills/engineering/resolve-release/scripts/plan-uv.test.mjs +291 -0
  503. package/skills/engineering/resolve-release/scripts/plan.mjs +806 -0
  504. package/skills/engineering/resolve-release/scripts/plan.test.mjs +428 -0
  505. package/skills/engineering/rush-issues/LICENSE +3 -0
  506. package/skills/engineering/rush-issues/SKILL.md +58 -0
  507. package/skills/engineering/rush-issues/agents/openai.yaml +8 -0
  508. package/skills/engineering/rush-issues/rehydrate.mjs +10 -0
  509. package/skills/engineering/rush-issues/release-check.mjs +93 -0
  510. package/skills/engineering/rush-release/LICENSE +3 -0
  511. package/skills/engineering/rush-release/SKILL.md +35 -0
  512. package/skills/engineering/rush-release/agents/openai.yaml +8 -0
  513. package/skills/engineering/rush-release/release-check.mjs +93 -0
  514. package/skills/engineering/test-quality/LICENSE +3 -0
  515. package/skills/engineering/test-quality/SKILL.md +79 -0
  516. package/skills/engineering/test-quality/references/audit.md +80 -0
  517. package/skills/engineering/test-quality/references/lint.md +24 -0
  518. package/skills/engineering/test-quality/references/principles.md +143 -0
  519. package/skills/engineering/test-quality/references/testing-conventions.md +9 -0
  520. package/skills/engineering/test-quality/release-check.mjs +93 -0
  521. package/skills/engineering/test-quality/scripts/lint-tests.mjs +574 -0
  522. package/skills/engineering/test-quality/scripts/lint-tests.test.mjs +591 -0
  523. package/skills/engineering/test-quality/scripts/mock-policy-config.mjs +176 -0
  524. package/skills/engineering/test-quality/scripts/mock-policy-config.test.mjs +119 -0
  525. package/skills/engineering/test-quality/scripts/persist-conventions-core.mjs +117 -0
  526. package/skills/engineering/test-quality/scripts/persist-conventions.mjs +17 -0
  527. package/skills/engineering/test-quality/scripts/persist-conventions.test.mjs +104 -0
  528. package/skills/risk-compliance/compliance-audit/LICENSE +3 -0
  529. package/skills/risk-compliance/compliance-audit/SKILL.md +439 -0
  530. package/skills/risk-compliance/compliance-audit/references/findings-schema.md +293 -0
  531. package/skills/risk-compliance/compliance-audit/references/normalization-rules.md +45 -0
  532. package/skills/risk-compliance/compliance-audit/references/vars-checklist.md +1068 -0
  533. package/skills/risk-compliance/compliance-audit/references/vars-findings-schema.md +398 -0
  534. package/skills/risk-compliance/compliance-audit/release-check.mjs +93 -0
  535. package/skills/risk-compliance/compliance-audit/scripts/compliance-audit.test.mjs +14 -0
  536. package/skills/risk-compliance/compliance-audit/scripts/export_tracker_xlsx.py +221 -0
  537. package/skills/risk-compliance/compliance-audit/scripts/reconcile_tracker.py +458 -0
  538. package/skills/risk-compliance/compliance-audit/scripts/test_export_tracker_xlsx.py +97 -0
  539. package/skills/risk-compliance/compliance-audit/scripts/test_validate_tracker.py +157 -0
  540. package/skills/risk-compliance/compliance-audit/scripts/validate_tracker.py +829 -0
  541. package/skills/risk-compliance/contract-compare/LICENSE +3 -0
  542. package/skills/risk-compliance/contract-compare/SKILL.md +199 -0
  543. package/skills/risk-compliance/contract-compare/references/comparison-dimensions.md +209 -0
  544. package/skills/risk-compliance/contract-compare/release-check.mjs +93 -0
  545. package/skills/risk-compliance/contract-review/LICENSE +3 -0
  546. package/skills/risk-compliance/contract-review/SKILL.md +240 -0
  547. package/skills/risk-compliance/contract-review/references/review-criteria.md +223 -0
  548. package/skills/risk-compliance/contract-review/release-check.mjs +93 -0
  549. package/skills/risk-compliance/doc-authenticity/LICENSE +3 -0
  550. package/skills/risk-compliance/doc-authenticity/SKILL.md +160 -0
  551. package/skills/risk-compliance/doc-authenticity/agents/openai.yaml +8 -0
  552. package/skills/risk-compliance/doc-authenticity/manual-invocation.test.mjs +12 -0
  553. package/skills/risk-compliance/doc-authenticity/references/agreement.md +66 -0
  554. package/skills/risk-compliance/doc-authenticity/references/bank-statement.md +150 -0
  555. package/skills/risk-compliance/doc-authenticity/references/forensic-signals.md +157 -0
  556. package/skills/risk-compliance/doc-authenticity/references/image-signals.md +114 -0
  557. package/skills/risk-compliance/doc-authenticity/references/invoice.md +66 -0
  558. package/skills/risk-compliance/doc-authenticity/references/issuer-profiles.md +86 -0
  559. package/skills/risk-compliance/doc-authenticity/references/verdict-and-report.md +95 -0
  560. package/skills/risk-compliance/doc-authenticity/references/verdict.schema.json +81 -0
  561. package/skills/risk-compliance/doc-authenticity/release-check.mjs +93 -0
  562. package/skills/risk-compliance/doc-authenticity/scripts/image_forensics.py +294 -0
  563. package/skills/risk-compliance/doc-authenticity/scripts/pdf_forensics.py +401 -0
  564. package/skills/risk-compliance/doc-authenticity/scripts/validate_codes.py +224 -0
  565. package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.py +308 -0
  566. package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.test.mjs +212 -0
  567. package/skills/risk-compliance/fraud-analysis/LICENSE +3 -0
  568. package/skills/risk-compliance/fraud-analysis/SKILL.md +114 -0
  569. package/skills/risk-compliance/fraud-analysis/references/archetypes/authorized-payment-scam.md +41 -0
  570. package/skills/risk-compliance/fraud-analysis/references/archetypes/first-party-fraud.md +44 -0
  571. package/skills/risk-compliance/fraud-analysis/references/archetypes/third-party-fraud.md +27 -0
  572. package/skills/risk-compliance/fraud-analysis/references/contexts/bank-transfer.md +24 -0
  573. package/skills/risk-compliance/fraud-analysis/references/contexts/card-payment.md +30 -0
  574. package/skills/risk-compliance/fraud-analysis/references/contexts/payment-collection.md +20 -0
  575. package/skills/risk-compliance/fraud-analysis/references/contexts/payout.md +20 -0
  576. package/skills/risk-compliance/fraud-analysis/references/feature-engineering.md +158 -0
  577. package/skills/risk-compliance/fraud-analysis/references/mechanisms/account-takeover.md +36 -0
  578. package/skills/risk-compliance/fraud-analysis/references/report-rationale.md +45 -0
  579. package/skills/risk-compliance/fraud-analysis/references/report-template.md +190 -0
  580. package/skills/risk-compliance/fraud-analysis/references/review-checklist.md +175 -0
  581. package/skills/risk-compliance/fraud-analysis/references/taxonomy.md +79 -0
  582. package/skills/risk-compliance/fraud-analysis/references/terminology.md +108 -0
  583. package/skills/risk-compliance/fraud-analysis/references/workflow.md +175 -0
  584. package/skills/risk-compliance/fraud-analysis/release-check.mjs +93 -0
  585. package/skills/risk-compliance/payment-analysis/LICENSE +3 -0
  586. package/skills/risk-compliance/payment-analysis/SKILL.md +180 -0
  587. package/skills/risk-compliance/payment-analysis/references/auth-rate-actions.md +30 -0
  588. package/skills/risk-compliance/payment-analysis/references/chargebacks.md +208 -0
  589. package/skills/risk-compliance/payment-analysis/references/chinese-polish.md +51 -0
  590. package/skills/risk-compliance/payment-analysis/references/event-layers.md +127 -0
  591. package/skills/risk-compliance/payment-analysis/references/fx.md +59 -0
  592. package/skills/risk-compliance/payment-analysis/references/journey.md +79 -0
  593. package/skills/risk-compliance/payment-analysis/references/metrics.md +67 -0
  594. package/skills/risk-compliance/payment-analysis/references/report-template.md +119 -0
  595. package/skills/risk-compliance/payment-analysis/references/terminology.md +95 -0
  596. package/skills/risk-compliance/payment-analysis/references/visualization.md +56 -0
  597. package/skills/risk-compliance/payment-analysis/release-check.mjs +93 -0
  598. package/skills/risk-compliance/report-writing/LICENSE +3 -0
  599. package/skills/risk-compliance/report-writing/SKILL.md +220 -0
  600. package/skills/risk-compliance/report-writing/references/audit-report-template.md +121 -0
  601. package/skills/risk-compliance/report-writing/references/external-assessment-template.md +167 -0
  602. package/skills/risk-compliance/report-writing/references/management-report-template.md +145 -0
  603. package/skills/risk-compliance/report-writing/references/vars-report-template.md +224 -0
  604. package/skills/risk-compliance/report-writing/release-check.mjs +93 -0
  605. package/skills/risk-compliance/similar-domain/LICENSE +3 -0
  606. package/skills/risk-compliance/similar-domain/SKILL.md +203 -0
  607. package/skills/risk-compliance/similar-domain/references/entities.md +67 -0
  608. package/skills/risk-compliance/similar-domain/references/fingerprints.md +134 -0
  609. package/skills/risk-compliance/similar-domain/references/pivots.md +82 -0
  610. package/skills/risk-compliance/similar-domain/references/report-template.md +93 -0
  611. package/skills/risk-compliance/similar-domain/references/scoring.md +130 -0
  612. package/skills/risk-compliance/similar-domain/references/terminology.md +41 -0
  613. package/skills/risk-compliance/similar-domain/references/workflow.md +85 -0
  614. package/skills/risk-compliance/similar-domain/release-check.mjs +93 -0
  615. package/skills/risk-compliance/tld-detection/LICENSE +3 -0
  616. package/skills/risk-compliance/tld-detection/SKILL.md +245 -0
  617. package/skills/risk-compliance/tld-detection/agents/openai.yaml +9 -0
  618. package/skills/risk-compliance/tld-detection/references/chinese-polish.md +55 -0
  619. package/skills/risk-compliance/tld-detection/references/clean.md +51 -0
  620. package/skills/risk-compliance/tld-detection/references/clustering.md +235 -0
  621. package/skills/risk-compliance/tld-detection/references/decision.md +189 -0
  622. package/skills/risk-compliance/tld-detection/references/fx-conversion.md +232 -0
  623. package/skills/risk-compliance/tld-detection/references/report-template.md +272 -0
  624. package/skills/risk-compliance/tld-detection/references/streams.md +141 -0
  625. package/skills/risk-compliance/tld-detection/references/terminology.md +95 -0
  626. package/skills/risk-compliance/tld-detection/references/workflow.md +95 -0
  627. package/skills/risk-compliance/tld-detection/release-check.mjs +93 -0
  628. package/skills/risk-compliance/tld-detection/scripts/detect_ladders.py +390 -0
  629. package/skills/risk-compliance/tld-detection/scripts/detect_ladders.test.mjs +14 -0
  630. package/skills/risk-compliance/tld-detection/scripts/detect_streams.py +650 -0
  631. package/skills/risk-compliance/tld-detection/scripts/detect_streams.test.mjs +14 -0
  632. package/skills/risk-compliance/tld-detection/tests/test_detect_ladders.py +134 -0
  633. package/skills/risk-compliance/tld-detection/tests/test_detect_streams.py +520 -0
@@ -0,0 +1,384 @@
1
+ #!/usr/bin/env node
2
+ // Bring reviews posted on the PR on GitHub into a resolve-issues review round.
3
+ //
4
+ // github-reviews.mjs merge --pr <owner/name#N> --head <sha> --review <run>/reviews/<unit>-r<N>.json
5
+ // [--prior <reviews/<unit>-r<N-1>.json>]
6
+ // github-reviews.mjs structured --pr <owner/name#N> --head <sha> --out <run>/reviews/<unit>-r<N>.json
7
+ // --unit <id> --round <N> --fix-round <F>
8
+ //
9
+ // Only reviews and comments from an account with write access to the repository, other
10
+ // than the PR author, count; the rest are listed under `external.ignored`.
11
+ //
12
+ // merge: add the PR's blocking GitHub feedback to the round's result file, in place, before
13
+ // review-verdict.mjs reads it. Rerunning it on the same file replaces what it added.
14
+ // - A reviewer whose latest review requests changes is a P1 finding `gh-review-<id>`.
15
+ // Once a fix brief has carried it, it is `awaiting` that reviewer's re-review instead.
16
+ // - An inline thread, review body, or PR comment whose first line is tagged P0 or P1
17
+ // (`P1:`, `**P1**`, `[P1]`, a `![P1 Badge]` image, also inside `<sub>`) is a finding `gh-comment-<id>`, until
18
+ // its thread is resolved or a fix brief has carried it once. P2/P3 tags stay on GitHub.
19
+ // - A GitHub finding is handled once the prior round's fix brief (`<prior>.fix.json`,
20
+ // written only on a `fix` verdict) carried it; `external.handled` carries the set from
21
+ // round to round. An incomplete or handed-back round hands nothing on.
22
+ // - Every `gh-review-<n>`/`gh-comment-<n>` finding is this script's: it removes each one
23
+ // the file holds, the reviewer's carried-forward copies included, and adds the current
24
+ // set, each with a trigger, rule, and open path like any blocking finding.
25
+ // Every review, thread and comment is read, page by page. A permission lookup that fails
26
+ // for any reason but "not a user" stops the merge (exit 2) rather than drop feedback.
27
+ // Exit 0 merged · 2 bad command or unreadable file or GitHub failure.
28
+ //
29
+ // structured: write a GitHub-posted better-review result for --head as this round's result
30
+ // file, so no reviewer is spawned. The result is a comment or review body holding
31
+ // STRUCTURED_MARKER and one ```json block; its `head` must be --head, and it must be
32
+ // complete by the reviewer's own rules (complete, every pass, checks run). The file takes
33
+ // --unit, --round and --fix-round from the caller and records its `source`.
34
+ // Exit 0 whenever it ran (`found: false` when none is found) · 2 bad command or GitHub failure.
35
+
36
+ import { execFileSync } from 'node:child_process'
37
+ import { readFileSync, writeFileSync } from 'node:fs'
38
+ import { basename, dirname, join } from 'node:path'
39
+
40
+ import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
41
+ import { reviewCompletionProblems } from '../../engineering-runtime/scripts/review-completion.mjs'
42
+
43
+ export const STRUCTURED_MARKER = '<!-- better-review-result -->'
44
+ // GitHub's base access level; `role_name` may be a custom role's name.
45
+ export const WRITE_PERMISSIONS = Object.freeze(['admin', 'write'])
46
+ export const GITHUB_ID = /^gh-(?:review|comment)-\d+$/
47
+ const BLOCKING_TAG = /^(?:[\s*_[(!]|<[^>\n]+>)*(P[0-3])\b/
48
+
49
+ export class GithubReviewsError extends Error {}
50
+
51
+ const isText = (value) => typeof value === 'string' && value.trim() !== ''
52
+ const firstLine = (body) => String(body ?? '').trim().split('\n', 1)[0]
53
+
54
+ // The P0-P3 tag a comment's first line opens with, or null.
55
+ export function severityTag(body) {
56
+ const m = firstLine(body).match(BLOCKING_TAG)
57
+ return m ? m[1] : null
58
+ }
59
+
60
+ export function parsePr(text) {
61
+ const m = String(text ?? '').match(/^([\w.-]+)\/([\w.-]+)#(\d+)$/)
62
+ if (!m) throw new GithubReviewsError(`--pr must be <owner/name#N>, got ${JSON.stringify(text)}`)
63
+ return { owner: m[1], name: m[2], number: Number(m[3]) }
64
+ }
65
+
66
+ // Split the PR's authors into trusted (write access, not the PR author) and ignored.
67
+ function trustOf(login, { prAuthor, permissionOf }) {
68
+ if (!isText(login)) return 'no author'
69
+ if (login === prAuthor) return 'PR author'
70
+ const permission = permissionOf(login)
71
+ return WRITE_PERMISSIONS.includes(permission) ? null : `permission ${permission ?? 'none'}`
72
+ }
73
+
74
+ // The result a body posts: the marker and a JSON block shaped like a result (a HEAD and a
75
+ // findings array); null otherwise, so any other comment is read as feedback.
76
+ function structuredJson(body) {
77
+ const text = String(body ?? '')
78
+ if (!text.includes(STRUCTURED_MARKER)) return null
79
+ const block = text.match(/```json\s*\n([\s\S]*?)\n```/)
80
+ if (!block) return null
81
+ let result
82
+ try { result = JSON.parse(block[1]) } catch { return null }
83
+ return isText(result?.head) && Array.isArray(result?.findings) ? result : null
84
+ }
85
+
86
+ // The blocking-finding shape review-verdict.mjs requires of every P0/P1.
87
+ function shaped(finding, what, where) {
88
+ return {
89
+ ...finding,
90
+ trigger: `${finding.author}'s ${what} on GitHub: ${finding.url}`,
91
+ rule: 'blocking review feedback on the PR is resolved before merge',
92
+ paths: [{ path: where || finding.url || 'the PR', status: 'open' }],
93
+ }
94
+ }
95
+
96
+ function summaryOf(body, fallback) {
97
+ const line = firstLine(body).replace(/!\[[^\]]*\]\([^)]*\)/g, '').replace(/<[^>]+>/g, '').replace(/[*_[\]]/g, '')
98
+ .replace(/^\s*P[0-3]\b[:\s-]*/, '').trim()
99
+ return line || fallback
100
+ }
101
+
102
+ /**
103
+ * The PR's blocking GitHub feedback for --head. `pr` is the GraphQL pullRequest node;
104
+ * `handled` holds the ids a fix brief has already carried.
105
+ */
106
+ export function collectExternal(pr, head, { permissionOf, handled = new Set() }) {
107
+ const prAuthor = pr?.author?.login ?? null
108
+ const trustCache = new Map()
109
+ const trusted = (login) => {
110
+ if (!trustCache.has(login)) trustCache.set(login, trustOf(login, { prAuthor, permissionOf }))
111
+ return trustCache.get(login)
112
+ }
113
+ const findings = []
114
+ const awaiting = []
115
+ const ignored = []
116
+ const ignore = (login, url, reason) => ignored.push({ author: login ?? null, url: url ?? null, reason })
117
+
118
+ // A reviewer's latest decisive review is their standing state, as GitHub counts it.
119
+ const latest = new Map()
120
+ for (const review of pr?.reviews?.nodes ?? []) {
121
+ const login = review?.author?.login
122
+ if (!['APPROVED', 'CHANGES_REQUESTED', 'DISMISSED'].includes(review?.state)) continue
123
+ const before = latest.get(login)
124
+ if (!before || String(review.submittedAt) >= String(before.submittedAt)) latest.set(login, review)
125
+ }
126
+ for (const [login, review] of latest) {
127
+ if (review.state !== 'CHANGES_REQUESTED') continue
128
+ const reason = trusted(login)
129
+ if (reason) { ignore(login, review.url, reason); continue }
130
+ const id = `gh-review-${review.databaseId}`
131
+ if (handled.has(id)) {
132
+ awaiting.push({ id, author: login, url: review.url, commit: review.commit?.oid ?? null })
133
+ continue
134
+ }
135
+ findings.push(shaped({
136
+ id, severity: 'P1', pass: 'external', source: 'github', author: login, url: review.url,
137
+ commit: review.commit?.oid ?? null,
138
+ summary: summaryOf(review.body, `${login} requested changes`),
139
+ ...(isText(review.body) ? { detail: review.body.trim() } : {}),
140
+ }, 'changes request'))
141
+ }
142
+
143
+ const tagged = (node, extra = {}) => {
144
+ const body = node?.body
145
+ if (structuredJson(body)) return
146
+ const severity = severityTag(body)
147
+ if (severity !== 'P0' && severity !== 'P1') return
148
+ const login = node?.author?.login
149
+ const reason = trusted(login)
150
+ if (reason) { ignore(login, node.url, reason); return }
151
+ const id = `gh-comment-${node.databaseId}`
152
+ if (handled.has(id)) return
153
+ const where = isText(extra.file) ? `${extra.file}${extra.line ? `:${extra.line}` : ''}` : null
154
+ findings.push(shaped({
155
+ id, severity, pass: 'external', source: 'github', author: login, url: node.url,
156
+ summary: summaryOf(body, `${login} flagged ${severity}`), detail: String(body).trim(), ...extra,
157
+ }, `${severity} comment`, where))
158
+ }
159
+ for (const thread of pr?.reviewThreads?.nodes ?? []) {
160
+ if (thread?.isResolved) continue
161
+ const comment = thread?.comments?.nodes?.[0]
162
+ if (!comment) continue
163
+ tagged(comment, {
164
+ ...(isText(comment.path) ? { file: comment.path } : {}),
165
+ ...(Number.isSafeInteger(comment.line ?? comment.originalLine) ? { line: comment.line ?? comment.originalLine } : {}),
166
+ commit: comment.originalCommit?.oid ?? comment.commit?.oid ?? null,
167
+ })
168
+ }
169
+ // A changes-requested review is already its reviewer's finding.
170
+ for (const review of pr?.reviews?.nodes ?? []) {
171
+ if (review?.state !== 'CHANGES_REQUESTED') tagged(review, { commit: review?.commit?.oid ?? null })
172
+ }
173
+ for (const comment of pr?.comments?.nodes ?? []) tagged(comment)
174
+
175
+ return { prAuthor, head, findings, awaiting, ignored }
176
+ }
177
+
178
+ // Ids a fix brief has carried: the prior round's ledger plus the GitHub findings in the
179
+ // prior round's fix brief, which exists only when its verdict sent them to a fix worker.
180
+ export function handledIds(prior, priorFixBrief = null) {
181
+ const ids = new Set(Array.isArray(prior?.external?.handled) ? prior.external.handled.filter((id) => GITHUB_ID.test(id)) : [])
182
+ for (const f of Array.isArray(priorFixBrief?.fix) ? priorFixBrief.fix : []) {
183
+ if (isText(f?.id) && GITHUB_ID.test(f.id)) ids.add(f.id)
184
+ }
185
+ return ids
186
+ }
187
+
188
+ /** The review file with this round's GitHub feedback in place of any added before. */
189
+ export function mergeExternal(review, external, handled) {
190
+ // GitHub is the authority on its own findings, so drop any copy the reviewer carried.
191
+ const own = (Array.isArray(review?.findings) ? review.findings : []).filter((f) => !GITHUB_ID.test(String(f?.id)))
192
+ return {
193
+ ...review,
194
+ findings: [...own, ...external.findings],
195
+ external: {
196
+ added: external.findings.map((f) => f.id),
197
+ head: external.head,
198
+ prAuthor: external.prAuthor,
199
+ awaiting: external.awaiting,
200
+ handled: [...handled].sort(),
201
+ ignored: external.ignored,
202
+ },
203
+ }
204
+ }
205
+
206
+ /**
207
+ * The newest trusted GitHub-posted better-review result for --head, or null. Bodies
208
+ * without the marker, with no JSON block, or of another HEAD are passed over.
209
+ */
210
+ export function findStructured(pr, head, { permissionOf }) {
211
+ const prAuthor = pr?.author?.login ?? null
212
+ const bodies = [
213
+ ...(pr?.reviews?.nodes ?? []).map((n) => ({ ...n, at: n?.submittedAt })),
214
+ ...(pr?.comments?.nodes ?? []).map((n) => ({ ...n, at: n?.createdAt })),
215
+ ].filter((n) => String(n?.body ?? '').includes(STRUCTURED_MARKER))
216
+ .sort((a, b) => String(b.at).localeCompare(String(a.at)))
217
+ for (const node of bodies) {
218
+ const result = structuredJson(node.body)
219
+ if (result?.head !== head) continue
220
+ // Unlike a legacy local record, a posted result must say it is complete.
221
+ if (result.complete !== true || reviewCompletionProblems(result, { requirePasses: true }).length) continue
222
+ if (trustOf(node?.author?.login, { prAuthor, permissionOf })) continue
223
+ return { result, source: { kind: 'github', author: node.author.login, url: node.url ?? null } }
224
+ }
225
+ return null
226
+ }
227
+
228
+ const CONNECTIONS = {
229
+ reviews: 'databaseId author { login } state body submittedAt url commit { oid }',
230
+ reviewThreads: `isResolved isOutdated comments(first: 1) {
231
+ nodes { databaseId author { login } body path line originalLine url commit { oid } originalCommit { oid } } }`,
232
+ comments: 'databaseId author { login } body createdAt url',
233
+ }
234
+ const pageQuery = (field) => `query($owner: String!, $name: String!, $number: Int!, $after: String) {
235
+ repository(owner: $owner, name: $name) {
236
+ pullRequest(number: $number) {
237
+ author { login }
238
+ ${field}(first: 100, after: $after) { pageInfo { hasNextPage endCursor } nodes { ${CONNECTIONS[field]} } }
239
+ }
240
+ }
241
+ }`
242
+
243
+ function gh(args) {
244
+ try {
245
+ return execFileSync('gh', args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] })
246
+ } catch (error) {
247
+ throw new GithubReviewsError(`gh ${args[0]} ${args[1] ?? ''} failed: ${String(error.stderr || error.message).trim()}`)
248
+ }
249
+ }
250
+
251
+ // Every node of each connection, page by page; `query` runs one GraphQL page.
252
+ export function fetchPr({ owner, name, number }, query = ghGraphql) {
253
+ const pr = {}
254
+ for (const field of Object.keys(CONNECTIONS)) {
255
+ const nodes = []
256
+ let after = null
257
+ do {
258
+ const page = query(pageQuery(field), { owner, name, number, after })?.data?.repository?.pullRequest
259
+ if (!page) throw new GithubReviewsError(`no pull request ${owner}/${name}#${number}`)
260
+ pr.author = page.author
261
+ const connection = page[field]
262
+ if (!Array.isArray(connection?.nodes)) throw new GithubReviewsError(`no ${field} in the GitHub response`)
263
+ nodes.push(...connection.nodes)
264
+ after = connection.pageInfo?.hasNextPage ? connection.pageInfo.endCursor : null
265
+ if (connection.pageInfo?.hasNextPage && !isText(after)) throw new GithubReviewsError(`${field}: a next page without a cursor`)
266
+ } while (after)
267
+ pr[field] = { nodes }
268
+ }
269
+ return pr
270
+ }
271
+
272
+ function ghGraphql(query, { owner, name, number, after }) {
273
+ const args = ['api', 'graphql', '-f', `query=${query}`, '-F', `owner=${owner}`, '-F', `name=${name}`, '-F', `number=${number}`]
274
+ if (after) args.push('-f', `after=${after}`)
275
+ return JSON.parse(gh(args))
276
+ }
277
+
278
+ // A bot answers 200 with permission "none"; a login that is no user answers 404. Any other
279
+ // failure is unknown access, so it stops the merge.
280
+ export function permissionFetcher({ owner, name }, api = (path) => gh(['api', path])) {
281
+ return (login) => {
282
+ let text
283
+ try {
284
+ text = api(`repos/${owner}/${name}/collaborators/${encodeURIComponent(login)}/permission`)
285
+ } catch (error) {
286
+ if (/\(HTTP 404\)/.test(error.message)) return 'none'
287
+ throw new GithubReviewsError(`cannot tell ${login}'s access: ${error.message}`)
288
+ }
289
+ const out = JSON.parse(text)
290
+ return out?.permission || 'none'
291
+ }
292
+ }
293
+
294
+ const USAGE = `usage:
295
+ github-reviews.mjs merge --pr <owner/name#N> --head <sha> --review <reviews/<unit>-r<N>.json> [--prior <file>]
296
+ github-reviews.mjs structured --pr <owner/name#N> --head <sha> --out <file> --unit <id> --round <N> --fix-round <F>`
297
+
298
+ function parseArgs(argv) {
299
+ const [mode, ...rest] = argv
300
+ const opts = { mode }
301
+ for (let i = 0; i < rest.length; i += 2) {
302
+ if (!rest[i]?.startsWith('--') || rest[i + 1] === undefined) throw new GithubReviewsError(USAGE)
303
+ opts[rest[i].slice(2)] = rest[i + 1]
304
+ }
305
+ const need = { merge: ['pr', 'head', 'review'], structured: ['pr', 'head', 'out', 'unit', 'round', 'fix-round'] }[mode]
306
+ if (!need || need.some((k) => !isText(opts[k]))) throw new GithubReviewsError(USAGE)
307
+ return opts
308
+ }
309
+
310
+ function readJson(path) {
311
+ try {
312
+ return JSON.parse(readFileSync(path, 'utf8'))
313
+ } catch (error) {
314
+ throw new GithubReviewsError(`cannot read ${path}: ${error.message}`)
315
+ }
316
+ }
317
+
318
+ function priorPath(opts) {
319
+ if (opts.prior) return opts.prior
320
+ const m = basename(opts.review).match(/^(.*-r)(\d+)\.json$/)
321
+ return m && Number(m[2]) >= 2 ? join(dirname(opts.review), `${m[1]}${Number(m[2]) - 1}.json`) : null
322
+ }
323
+
324
+ function counter(text, flag) {
325
+ const n = Number(text)
326
+ if (!Number.isSafeInteger(n) || n < 1) throw new GithubReviewsError(`--${flag} must be a positive integer`)
327
+ return n
328
+ }
329
+
330
+ export function run(argv, { fetch = fetchPr, permissions = permissionFetcher } = {}) {
331
+ const opts = parseArgs(argv)
332
+ const target = parsePr(opts.pr)
333
+ const pr = fetch(target)
334
+ const permissionOf = permissions(target)
335
+ if (opts.mode === 'structured') {
336
+ const found = findStructured(pr, opts.head, { permissionOf })
337
+ if (!found) return { out: { found: false, head: opts.head } }
338
+ const result = {
339
+ ...found.result,
340
+ unit: opts.unit,
341
+ round: counter(opts.round, 'round'),
342
+ fixRound: counter(opts['fix-round'], 'fix-round'),
343
+ source: found.source,
344
+ }
345
+ writeFileSync(opts.out, `${JSON.stringify(result, null, 2)}\n`)
346
+ return { out: { found: true, path: opts.out, source: found.source } }
347
+ }
348
+ const review = readJson(opts.review)
349
+ const path = priorPath(opts)
350
+ let prior = null
351
+ if (path) {
352
+ try { prior = JSON.parse(readFileSync(path, 'utf8')) } catch (error) {
353
+ if (opts.prior || error.code !== 'ENOENT') throw new GithubReviewsError(`cannot read prior ${path}: ${error.message}`)
354
+ }
355
+ }
356
+ let priorFixBrief = null
357
+ if (path) {
358
+ const briefPath = join(dirname(path), `${basename(path, '.json')}.fix.json`)
359
+ try { priorFixBrief = JSON.parse(readFileSync(briefPath, 'utf8')) } catch (error) {
360
+ if (error.code !== 'ENOENT') throw new GithubReviewsError(`cannot read ${briefPath}: ${error.message}`)
361
+ }
362
+ }
363
+ const handled = handledIds(prior, priorFixBrief)
364
+ const external = collectExternal(pr, opts.head, { permissionOf, handled })
365
+ const merged = mergeExternal(review, external, handled)
366
+ writeFileSync(opts.review, `${JSON.stringify(merged, null, 2)}\n`)
367
+ return {
368
+ out: {
369
+ findings: external.findings.map(({ id, severity, author }) => ({ id, severity, author })),
370
+ awaiting: external.awaiting,
371
+ ignored: external.ignored.length,
372
+ },
373
+ }
374
+ }
375
+
376
+ if (isMainModule(import.meta.url)) {
377
+ try {
378
+ const { out } = run(process.argv.slice(2))
379
+ process.stdout.write(`${JSON.stringify(out, null, 2)}\n`)
380
+ } catch (error) {
381
+ process.stderr.write(`${error instanceof GithubReviewsError ? error.message : error.stack}\n`)
382
+ process.exitCode = 2
383
+ }
384
+ }
@@ -0,0 +1,264 @@
1
+ import { test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
4
+ import { tmpdir } from 'node:os'
5
+ import { join } from 'node:path'
6
+
7
+ import {
8
+ collectExternal,
9
+ fetchPr,
10
+ permissionFetcher,
11
+ findStructured,
12
+ handledIds,
13
+ mergeExternal,
14
+ parsePr,
15
+ run,
16
+ severityTag,
17
+ STRUCTURED_MARKER,
18
+ } from './github-reviews.mjs'
19
+
20
+ const HEAD = 'abc1234'
21
+ const ROLES = { vera932: 'write', alo335: 'admin', reader: 'read' }
22
+ const permissionOf = (login) => ROLES[login] ?? 'none'
23
+ const review = (id, login, state, body = '', at = '2026-10-08T10:00:00Z', commit = HEAD) => ({
24
+ databaseId: id, author: { login }, state, body, submittedAt: at, url: `https://github.com/o/r/pull/1#pullrequestreview-${id}`, commit: { oid: commit },
25
+ })
26
+ const thread = (id, login, body, extra = {}) => ({
27
+ isResolved: false, isOutdated: false, ...extra,
28
+ comments: { nodes: [{ databaseId: id, author: { login }, body, path: 'src/a.ts', line: 12, url: `https://github.com/o/r/pull/1#discussion_r${id}`, originalCommit: { oid: HEAD } }] },
29
+ })
30
+ const pr = (parts = {}) => ({
31
+ author: { login: 'cory-bot' },
32
+ reviews: { nodes: parts.reviews ?? [] },
33
+ reviewThreads: { nodes: parts.threads ?? [] },
34
+ comments: { nodes: parts.comments ?? [] },
35
+ })
36
+
37
+ test('severity tags are read from the first line in the usual forms only', () => {
38
+ for (const [body, tag] of [
39
+ ['P1: refund sign flips', 'P1'], ['**P0** token leaks', 'P0'], ['[P1] missing check', 'P1'],
40
+ ['![P1 Badge](https://img.shields.io/badge/P1-orange) Wrong total', 'P1'], ['P2: rename', 'P2'],
41
+ ['**<sub><sub>![P1 Badge](https://img.shields.io/badge/P1-orange?style=flat)</sub></sub> Keep the lock**', 'P1'],
42
+ ['<!-- codex-pull-request-review-summary -->', null],
43
+ ['Pretty sure this is fine', null], ['P10 items', null], ['nit\nP1: on line two', null], ['', null],
44
+ ]) assert.equal(severityTag(body), tag, body)
45
+ })
46
+
47
+ test('a trusted changes request is a P1 finding; approvals, dismissals and later approvals clear it', () => {
48
+ const blocked = collectExternal(pr({ reviews: [review(11, 'vera932', 'CHANGES_REQUESTED', 'Refund rounds the wrong way\nmore')] }), HEAD, { permissionOf })
49
+ assert.deepEqual(blocked.findings.map((f) => [f.id, f.severity, f.summary, f.author]), [['gh-review-11', 'P1', 'Refund rounds the wrong way', 'vera932']])
50
+ assert.match(blocked.findings[0].detail, /more/)
51
+ const cleared = collectExternal(pr({ reviews: [
52
+ review(11, 'vera932', 'CHANGES_REQUESTED', 'x', '2026-10-08T10:00:00Z'),
53
+ review(12, 'vera932', 'COMMENTED', 'thanks', '2026-10-08T11:00:00Z'),
54
+ review(13, 'vera932', 'APPROVED', '', '2026-10-08T12:00:00Z'),
55
+ ] }), HEAD, { permissionOf })
56
+ assert.deepEqual(cleared.findings, [])
57
+ const dismissed = collectExternal(pr({ reviews: [review(11, 'vera932', 'CHANGES_REQUESTED', 'x'), review(14, 'vera932', 'DISMISSED', '', '2026-10-08T12:00:00Z')] }), HEAD, { permissionOf })
58
+ assert.deepEqual(dismissed.findings, [])
59
+ })
60
+
61
+ test('only write access counts, and never the PR author', () => {
62
+ const external = collectExternal(pr({
63
+ reviews: [review(21, 'reader', 'CHANGES_REQUESTED', 'no'), review(22, 'cory-bot', 'CHANGES_REQUESTED', 'self')],
64
+ threads: [thread(31, 'codex-bot', 'P1: bot says'), thread(32, 'alo335', 'P1: maintainer says')],
65
+ }), HEAD, { permissionOf })
66
+ assert.deepEqual(external.findings.map((f) => f.id), ['gh-comment-32'])
67
+ assert.deepEqual(external.ignored.map((i) => [i.author, i.reason]).sort(), [
68
+ ['codex-bot', 'permission none'], ['cory-bot', 'PR author'], ['reader', 'permission read'],
69
+ ])
70
+ })
71
+
72
+ test('tagged comments block until resolved; P2/P3 and untagged comments never do', () => {
73
+ const external = collectExternal(pr({
74
+ threads: [
75
+ thread(41, 'vera932', '**P0** tenant check missing'),
76
+ thread(42, 'vera932', 'P1: resolved already', { isResolved: true }),
77
+ thread(43, 'vera932', 'P2: rename'),
78
+ thread(44, 'vera932', 'looks off to me'),
79
+ ],
80
+ reviews: [review(45, 'vera932', 'COMMENTED', '[P1] the export drops currency')],
81
+ comments: [{ databaseId: 46, author: { login: 'alo335' }, body: 'P1: migration not reversible', createdAt: '2026-10-08T10:00:00Z', url: 'u46' }],
82
+ }), HEAD, { permissionOf })
83
+ assert.deepEqual(external.findings.map((f) => [f.id, f.severity]), [['gh-comment-41', 'P0'], ['gh-comment-45', 'P1'], ['gh-comment-46', 'P1']])
84
+ assert.equal(external.findings[0].file, 'src/a.ts')
85
+ assert.equal(external.findings[0].line, 12)
86
+ const badge = collectExternal(pr({ threads: [thread(49, 'vera932', '**<sub><sub>![P1 Badge](https://img.shields.io/badge/P1-orange)</sub></sub> Keep the lock**')] }), HEAD, { permissionOf })
87
+ assert.equal(badge.findings[0].summary, 'Keep the lock')
88
+ })
89
+
90
+ test('a changes request whose body is tagged counts once', () => {
91
+ const external = collectExternal(pr({ reviews: [review(47, 'vera932', 'CHANGES_REQUESTED', 'P1: wrong total')] }), HEAD, { permissionOf })
92
+ assert.deepEqual(external.findings.map((f) => f.id), ['gh-review-47'])
93
+ })
94
+
95
+ test('after a fix brief carried them, a standing changes request is awaited and a comment is dropped', () => {
96
+ const handled = new Set(['gh-review-11', 'gh-comment-41'])
97
+ const external = collectExternal(pr({
98
+ reviews: [review(11, 'vera932', 'CHANGES_REQUESTED', 'x', '2026-10-08T10:00:00Z', 'old1234')],
99
+ threads: [thread(41, 'vera932', 'P1: still unresolved'), thread(48, 'vera932', 'P1: new one')],
100
+ }), HEAD, { permissionOf, handled })
101
+ assert.deepEqual(external.findings.map((f) => f.id), ['gh-comment-48'])
102
+ assert.deepEqual(external.awaiting.map((a) => [a.id, a.author, a.commit]), [['gh-review-11', 'vera932', 'old1234']])
103
+ const rerequested = collectExternal(pr({ reviews: [
104
+ review(11, 'vera932', 'CHANGES_REQUESTED', 'x', '2026-10-08T10:00:00Z', 'old1234'),
105
+ review(15, 'vera932', 'CHANGES_REQUESTED', 'still wrong', '2026-10-08T13:00:00Z'),
106
+ ] }), HEAD, { permissionOf, handled })
107
+ assert.deepEqual(rerequested.findings.map((f) => f.id), ['gh-review-15'], 'a new changes request is a new finding')
108
+ assert.deepEqual(rerequested.awaiting, [])
109
+ })
110
+
111
+ test('the handled ledger carries forward and merge replaces what it added before', () => {
112
+ const prior = { findings: [{ id: 'gh-review-11', severity: 'P1' }, { id: 'R1', severity: 'P1' }], external: { added: ['gh-review-11'], handled: ['gh-comment-41'] } }
113
+ const brief = { fix: [{ id: 'gh-review-11' }, { id: 'R1' }] }
114
+ assert.deepEqual([...handledIds(prior, brief)].sort(), ['gh-comment-41', 'gh-review-11'])
115
+ assert.deepEqual([...handledIds(prior, null)], ['gh-comment-41'], 'no fix brief: the prior round handed nothing on')
116
+ assert.deepEqual([...handledIds(null)], [])
117
+ const own = { id: 'R2', severity: 'P1' }
118
+ const stale = { id: 'gh-comment-1', severity: 'P1', source: 'github' }
119
+ const fresh = { findings: [{ id: 'gh-comment-2', severity: 'P1', source: 'github' }], awaiting: [], ignored: [], head: HEAD, prAuthor: 'cory-bot' }
120
+ const merged = mergeExternal({ version: 1, findings: [own, stale], external: { added: ['gh-comment-1'] } }, fresh, new Set(['gh-review-11']))
121
+ assert.deepEqual(merged.findings.map((f) => f.id), ['R2', 'gh-comment-2'])
122
+ assert.deepEqual(merged.external.added, ['gh-comment-2'])
123
+ assert.deepEqual(merged.external.handled, ['gh-review-11'])
124
+ })
125
+
126
+ const PASSES = { spec: 'done', correctness: 'done', tests: 'done', standards: 'done' }
127
+ const structuredBody = (head, extra = {}) => `${STRUCTURED_MARKER}\nReview by Agentier\n\n\`\`\`json\n${JSON.stringify({ version: 1, head, complete: true, passes: PASSES, findings: [], commands: ['npm test'], ...extra })}\n\`\`\`\n`
128
+
129
+ test('a structured result stands in only when trusted and of this HEAD, newest first', () => {
130
+ const comments = [
131
+ { databaseId: 51, author: { login: 'alo335' }, body: structuredBody(HEAD, { commands: ['older'] }), createdAt: '2026-10-08T10:00:00Z', url: 'u51' },
132
+ { databaseId: 52, author: { login: 'alo335' }, body: structuredBody(HEAD, { commands: ['newer'] }), createdAt: '2026-10-08T11:00:00Z', url: 'u52' },
133
+ { databaseId: 53, author: { login: 'alo335' }, body: structuredBody('other99'), createdAt: '2026-10-08T12:00:00Z', url: 'u53' },
134
+ { databaseId: 54, author: { login: 'reader' }, body: structuredBody(HEAD, { commands: ['untrusted'] }), createdAt: '2026-10-08T13:00:00Z', url: 'u54' },
135
+ { databaseId: 55, author: { login: 'cory-bot' }, body: structuredBody(HEAD, { commands: ['self'] }), createdAt: '2026-10-08T14:00:00Z', url: 'u55' },
136
+ { databaseId: 56, author: { login: 'alo335' }, body: `${STRUCTURED_MARKER}\n\`\`\`json\n{not json\n\`\`\``, createdAt: '2026-10-08T15:00:00Z', url: 'u56' },
137
+ ]
138
+ const found = findStructured(pr({ comments }), HEAD, { permissionOf })
139
+ assert.deepEqual(found.result.commands, ['newer'])
140
+ assert.deepEqual(found.source, { kind: 'github', author: 'alo335', url: 'u52' })
141
+ assert.equal(findStructured(pr({ comments: comments.slice(2) }), HEAD, { permissionOf }), null)
142
+ const tagged = collectExternal(pr({ comments: [{ ...comments[0], body: structuredBody(HEAD, { findings: [{ id: 'R1', severity: 'P1' }] }).replace('Review by', 'P1 Review by') }] }), HEAD, { permissionOf })
143
+ assert.deepEqual(tagged.findings, [], 'a structured result is never read as a tagged comment')
144
+ })
145
+
146
+ test('parsePr takes owner/name#N only', () => {
147
+ assert.deepEqual(parsePr('1aboveio/ai#1545'), { owner: '1aboveio', name: 'ai', number: 1545 })
148
+ assert.throws(() => parsePr('1545'), /owner\/name#N/)
149
+ })
150
+
151
+ test('run merges into the round file using the prior beside it, and writes a structured result with the caller\'s counters', (t) => {
152
+ const dir = mkdtempSync(join(tmpdir(), 'github-reviews-'))
153
+ t.after(() => rmSync(dir, { recursive: true, force: true }))
154
+ const deps = (node) => ({ fetch: () => node, permissions: () => permissionOf })
155
+ writeFileSync(join(dir, '7-r1.json'), JSON.stringify({ findings: [{ id: 'gh-review-11', severity: 'P1', source: 'github' }], external: { added: ['gh-review-11'] } }))
156
+ writeFileSync(join(dir, '7-r1.fix.json'), JSON.stringify({ fix: [{ id: 'gh-review-11' }] }))
157
+ const path = join(dir, '7-r2.json')
158
+ writeFileSync(path, JSON.stringify({ version: 1, unit: '7', round: 2, head: HEAD, findings: [] }))
159
+ const node = pr({ reviews: [review(11, 'vera932', 'CHANGES_REQUESTED', 'x', '2026-10-08T10:00:00Z', 'old1234')], threads: [thread(61, 'vera932', 'P1: new')] })
160
+ const merged = run(['merge', '--pr', 'o/r#1', '--head', HEAD, '--review', path], deps(node))
161
+ assert.deepEqual(merged.out.awaiting.map((a) => a.id), ['gh-review-11'])
162
+ const file = JSON.parse(readFileSync(path, 'utf8'))
163
+ assert.deepEqual(file.findings.map((f) => f.id), ['gh-comment-61'])
164
+ assert.deepEqual(file.external.awaiting.map((a) => a.id), ['gh-review-11'])
165
+ assert.deepEqual(file.external.handled, ['gh-review-11'])
166
+
167
+ const out = join(dir, '7-r3.json')
168
+ const none = run(['structured', '--pr', 'o/r#1', '--head', HEAD, '--out', out, '--unit', '7', '--round', '3', '--fix-round', '2'], deps(pr()))
169
+ assert.equal(none.out.found, false)
170
+ const withResult = pr({ comments: [{ databaseId: 71, author: { login: 'alo335' }, body: structuredBody(HEAD, { unit: 'theirs', round: 9, fixRound: 9 }), createdAt: '2026-10-08T10:00:00Z', url: 'u71' }] })
171
+ const written = run(['structured', '--pr', 'o/r#1', '--head', HEAD, '--out', out, '--unit', '7', '--round', '3', '--fix-round', '2'], deps(withResult))
172
+ assert.equal(written.out.found, true)
173
+ const result = JSON.parse(readFileSync(out, 'utf8'))
174
+ assert.deepEqual([result.unit, result.round, result.fixRound, result.source.author], ['7', 3, 2, 'alo335'])
175
+ assert.throws(() => run(['merge', '--pr', 'o/r#1', '--head', HEAD], deps(node)), /usage/)
176
+ })
177
+
178
+ test('a GitHub finding from an incomplete or handed-back round is not handled: it still blocks', (t) => {
179
+ const dir = mkdtempSync(join(tmpdir(), 'github-reviews-nofix-'))
180
+ t.after(() => rmSync(dir, { recursive: true, force: true }))
181
+ // round 1 was incomplete (or handed back): the verdict wrote no fix brief
182
+ writeFileSync(join(dir, '7-r1.json'), JSON.stringify({ complete: false, findings: [{ id: 'gh-comment-1', severity: 'P1', source: 'github' }], external: { added: ['gh-comment-1'] } }))
183
+ const path = join(dir, '7-r2.json')
184
+ writeFileSync(path, JSON.stringify({ version: 1, unit: '7', round: 2, head: HEAD, findings: [] }))
185
+ run(['merge', '--pr', 'o/r#1', '--head', HEAD, '--review', path], { fetch: () => pr({ threads: [thread(1, 'vera932', 'P1: still broken')] }), permissions: () => permissionOf })
186
+ assert.deepEqual(JSON.parse(readFileSync(path, 'utf8')).findings.map((f) => f.id), ['gh-comment-1'])
187
+ })
188
+
189
+ test('merge never removes a reviewer finding that only claims github provenance', () => {
190
+ const forged = { id: 'R1', severity: 'P1', summary: 'real local blocker', source: 'github' }
191
+ const merged = mergeExternal({ version: 1, findings: [forged] }, { findings: [], awaiting: [], ignored: [], head: HEAD }, new Set())
192
+ assert.deepEqual(merged.findings, [forged])
193
+ const faked = mergeExternal({ version: 1, findings: [forged], external: { added: ['R1'] } }, { findings: [], awaiting: [], ignored: [], head: HEAD }, new Set())
194
+ assert.deepEqual(faked.findings, [forged], 'listing a reviewer id under external.added does not make it removable')
195
+ })
196
+
197
+ test('every page is read: a standing changes request behind 100 comments still blocks', () => {
198
+ const standing = review(1, 'vera932', 'CHANGES_REQUESTED', 'no', '2026-10-08T09:00:00Z')
199
+ const chatter = Array.from({ length: 150 }, (_, i) => review(100 + i, 'alo335', 'COMMENTED', 'ok', '2026-10-08T10:00:00Z'))
200
+ const all = { reviews: [standing, ...chatter], reviewThreads: [], comments: [] }
201
+ const calls = []
202
+ const query = (text, { after }) => {
203
+ const field = Object.keys(all).find((f) => text.includes(`${f}(first: 100`))
204
+ calls.push([field, after])
205
+ const start = after ? Number(after) : 0
206
+ const nodes = all[field].slice(start, start + 100)
207
+ const next = start + 100 < all[field].length
208
+ return { data: { repository: { pullRequest: { author: { login: 'cory-bot' }, [field]: { nodes, pageInfo: { hasNextPage: next, endCursor: next ? String(start + 100) : null } } } } } }
209
+ }
210
+ const fetched = fetchPr({ owner: 'o', name: 'r', number: 1 }, query)
211
+ assert.equal(fetched.reviews.nodes.length, 151)
212
+ assert.deepEqual(calls.filter(([f]) => f === 'reviews'), [['reviews', null], ['reviews', '100']])
213
+ assert.deepEqual(collectExternal(fetched, HEAD, { permissionOf }).findings.map((f) => f.id), ['gh-review-1'])
214
+ assert.throws(() => fetchPr({ owner: 'o', name: 'r', number: 1 }, () => ({ data: { repository: { pullRequest: { reviews: { nodes: [], pageInfo: { hasNextPage: true, endCursor: null } } } } } })), /without a cursor/)
215
+ })
216
+
217
+ test('an access lookup that fails stops the merge; only "not a user" means no access', () => {
218
+ const failing = (message) => permissionFetcher({ owner: 'o', name: 'r' }, () => { throw new Error(message) })
219
+ assert.throws(() => failing('gh api failed: connection reset')('vera932'), /cannot tell vera932's access/)
220
+ assert.equal(failing('gh: x is not a user (HTTP 404)')('x'), 'none')
221
+ const answering = (body) => permissionFetcher({ owner: 'o', name: 'r' }, () => JSON.stringify(body))
222
+ assert.equal(answering({ permission: 'none', role_name: '' })('codex[bot]'), 'none', 'a bot answers 200 with no access')
223
+ assert.equal(answering({ permission: 'write', role_name: 'maintain' })('alo335'), 'write')
224
+ assert.equal(answering({ permission: 'write', role_name: 'Security reviewer' })('sec'), 'write', 'a custom role keeps its base access')
225
+ const blocked = () => collectExternal(pr({ reviews: [review(1, 'alo335', 'CHANGES_REQUESTED', 'no')] }), HEAD, { permissionOf: failing('HTTP 502') })
226
+ assert.throws(blocked, /cannot tell alo335's access/)
227
+ })
228
+
229
+ test('a GitHub finding the reviewer carried forward is replaced, not doubled or refused', () => {
230
+ const carried = { id: 'gh-comment-1', severity: 'P1', summary: 'carried by the reviewer', trigger: 't', rule: 'r', paths: [{ path: 'a', status: 'open' }] }
231
+ const current = collectExternal(pr({ threads: [thread(1, 'vera932', 'P1: still broken')] }), HEAD, { permissionOf })
232
+ const merged = mergeExternal({ version: 1, findings: [carried, { id: 'R2', severity: 'P3' }] }, current, new Set())
233
+ assert.deepEqual(merged.findings.map((f) => [f.id, f.summary]), [['R2', undefined], ['gh-comment-1', 'still broken']])
234
+ const resolved = mergeExternal({ version: 1, findings: [carried] }, collectExternal(pr(), HEAD, { permissionOf }), new Set())
235
+ assert.deepEqual(resolved.findings, [], 'GitHub decides whether its finding is still open')
236
+ })
237
+
238
+ test('GitHub findings carry the blocking shape, so the verdict needs no exemption', () => {
239
+ const [finding] = collectExternal(pr({ threads: [thread(1, 'vera932', 'P1: still broken')] }), HEAD, { permissionOf }).findings
240
+ assert.match(finding.trigger, /vera932's P1 comment on GitHub/)
241
+ assert.ok(finding.rule)
242
+ assert.deepEqual(finding.paths, [{ path: 'src/a.ts:12', status: 'open' }])
243
+ })
244
+
245
+ test('a comment that only mentions the marker is still read as feedback', () => {
246
+ for (const body of [
247
+ `P1: Parser drops feedback containing ${STRUCTURED_MARKER}`,
248
+ `P1: Parser drops feedback containing ${STRUCTURED_MARKER}\n\n\`\`\`json\n{}\n\`\`\``,
249
+ `P1: wrong\n${STRUCTURED_MARKER}\n\`\`\`json\n{"head":"${HEAD}"}\n\`\`\``,
250
+ ]) {
251
+ const external = collectExternal(pr({ threads: [thread(1, 'vera932', body)] }), HEAD, { permissionOf })
252
+ assert.deepEqual(external.findings.map((f) => f.id), ['gh-comment-1'], body)
253
+ }
254
+ })
255
+
256
+ test('a posted result must be complete by the reviewer\'s own rules', () => {
257
+ const posted = (extra) => pr({ comments: [{ databaseId: 1, author: { login: 'alo335' }, body: structuredBody(HEAD, extra), createdAt: '2026-10-08T10:00:00Z', url: 'u1' }] })
258
+ assert.ok(findStructured(posted({}), HEAD, { permissionOf }))
259
+ assert.equal(findStructured(posted({ complete: undefined, passes: undefined }), HEAD, { permissionOf }), null)
260
+ assert.equal(findStructured(posted({ passes: { ...PASSES, tests: 'partial' } }), HEAD, { permissionOf }), null)
261
+ assert.equal(findStructured(posted({ commands: [] }), HEAD, { permissionOf }), null)
262
+ assert.equal(findStructured(posted({ findings: undefined }), HEAD, { permissionOf }), null)
263
+ assert.equal(findStructured(posted({ complete: undefined }), HEAD, { permissionOf }), null, 'all passes done but no complete: true')
264
+ })