@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,117 @@
1
+ // Unit tests for the pure Perceivability cores (issue #196). Run: `node --test`.
2
+ // The Playwright wrappers (assertContrast / assertHiddenFromWrongRole /
3
+ // assertRemovedAfterMutation) need a real browser + @axe-core/playwright and are
4
+ // exercised downstream; the contrast math, axe classifier, and predicates are
5
+ // proven here without one.
6
+
7
+ import { test } from 'node:test'
8
+ import assert from 'node:assert/strict'
9
+ import {
10
+ relativeLuminance, contrastRatio, meetsContrast, classifyAxeContrast,
11
+ isEffectivelyHidden, isExemptFromContrast, assessElementContrast, AbstainError,
12
+ } from './presentation-perceivability.mjs'
13
+
14
+ const WHITE = [255, 255, 255]
15
+ const BLACK = [0, 0, 0]
16
+
17
+ // ---------- WCAG math ----------
18
+
19
+ test('relative luminance: white ~1, black 0', () => {
20
+ assert.ok(Math.abs(relativeLuminance(WHITE) - 1) < 1e-9)
21
+ assert.equal(relativeLuminance(BLACK), 0)
22
+ })
23
+
24
+ test('contrast ratio: black on white is 21:1, white on white is 1:1', () => {
25
+ assert.ok(Math.abs(contrastRatio(BLACK, WHITE) - 21) < 1e-6)
26
+ assert.ok(Math.abs(contrastRatio(WHITE, WHITE) - 1) < 1e-9)
27
+ assert.equal(contrastRatio(BLACK, WHITE), contrastRatio(WHITE, BLACK)) // symmetric
28
+ })
29
+
30
+ test('meetsContrast: black text on white passes AA', () => {
31
+ const r = meetsContrast(BLACK, WHITE)
32
+ assert.equal(r.threshold, 4.5)
33
+ assert.equal(r.pass, true)
34
+ })
35
+
36
+ test('meetsContrast: a faint gray fails both text and non-text', () => {
37
+ const faint = [170, 170, 170] // ~2.3:1 on white
38
+ assert.equal(meetsContrast(faint, WHITE).pass, false)
39
+ assert.equal(meetsContrast(faint, WHITE, { nonText: true }).pass, false)
40
+ })
41
+
42
+ test('meetsContrast: threshold flips between text (4.5) and non-text (3.0)', () => {
43
+ const mid = [130, 130, 130] // ~3.8:1 on white — between 3 and 4.5
44
+ const asText = meetsContrast(mid, WHITE)
45
+ const asUi = meetsContrast(mid, WHITE, { nonText: true })
46
+ assert.equal(asText.threshold, 4.5)
47
+ assert.equal(asText.pass, false, 'fails as normal text')
48
+ assert.equal(asUi.threshold, 3)
49
+ assert.equal(asUi.pass, true, 'passes as a UI component / large text')
50
+ })
51
+
52
+ // ---------- axe classifier ----------
53
+
54
+ test('classifyAxeContrast: no findings → pass', () => {
55
+ assert.equal(classifyAxeContrast({ violations: [], incomplete: [] }).status, 'pass')
56
+ assert.equal(classifyAxeContrast({}).status, 'pass')
57
+ })
58
+
59
+ test('classifyAxeContrast: a color-contrast violation → fail', () => {
60
+ const res = { violations: [{ id: 'color-contrast', nodes: [{ target: ['.btn'] }] }], incomplete: [] }
61
+ const c = classifyAxeContrast(res)
62
+ assert.equal(c.status, 'fail')
63
+ assert.equal(c.count, 1)
64
+ })
65
+
66
+ test('classifyAxeContrast: incomplete (gradient/image) → abstain, not pass', () => {
67
+ const res = { violations: [], incomplete: [{ id: 'color-contrast', nodes: [{ target: ['.hero'] }] }] }
68
+ assert.equal(classifyAxeContrast(res).status, 'abstain')
69
+ })
70
+
71
+ test('classifyAxeContrast: violation outranks incomplete; other rule ids ignored', () => {
72
+ const res = {
73
+ violations: [{ id: 'color-contrast', nodes: [{}] }, { id: 'label', nodes: [{}] }],
74
+ incomplete: [{ id: 'color-contrast', nodes: [{}] }],
75
+ }
76
+ const c = classifyAxeContrast(res)
77
+ assert.equal(c.status, 'fail')
78
+ assert.equal(c.count, 1, 'only color-contrast nodes counted')
79
+ })
80
+
81
+ // ---------- visibility / exemption predicates ----------
82
+
83
+ test('isEffectivelyHidden: catches display/visibility/opacity/aria/zero-size', () => {
84
+ assert.equal(isEffectivelyHidden({ style: { display: 'none' } }), true)
85
+ assert.equal(isEffectivelyHidden({ style: { visibility: 'hidden' } }), true)
86
+ assert.equal(isEffectivelyHidden({ style: { opacity: '0' } }), true)
87
+ assert.equal(isEffectivelyHidden({ ariaHidden: true }), true)
88
+ assert.equal(isEffectivelyHidden({ rect: { width: 0, height: 10 } }), true)
89
+ })
90
+
91
+ test('isEffectivelyHidden: a normal visible element is not hidden', () => {
92
+ assert.equal(isEffectivelyHidden({ style: { display: 'block', visibility: 'visible', opacity: '1' }, rect: { width: 100, height: 40 } }), false)
93
+ })
94
+
95
+ test('isExemptFromContrast: disabled controls are exempt (WCAG), normal are not', () => {
96
+ assert.equal(isExemptFromContrast({ disabled: true }), true)
97
+ assert.equal(isExemptFromContrast({ ariaDisabled: true }), true)
98
+ assert.equal(isExemptFromContrast({}), false)
99
+ })
100
+
101
+ test('assessElementContrast (manual/escalation path) composes exemption + threshold', () => {
102
+ // disabled → exempt, regardless of (bad) colors
103
+ assert.equal(assessElementContrast(WHITE, WHITE, { disabled: true }).status, 'exempt')
104
+ // enabled, good contrast → pass
105
+ assert.equal(assessElementContrast(BLACK, WHITE).status, 'pass')
106
+ // enabled, faint gray → fail, with ratio reported
107
+ const fail = assessElementContrast([170, 170, 170], WHITE)
108
+ assert.equal(fail.status, 'fail')
109
+ assert.ok(fail.ratio < fail.threshold)
110
+ })
111
+
112
+ test('AbstainError is distinguishable from a hard failure', () => {
113
+ const e = new AbstainError('x', { count: 2 })
114
+ assert.equal(e.abstain, true)
115
+ assert.equal(e.name, 'AbstainError')
116
+ assert.ok(e instanceof Error)
117
+ })
@@ -0,0 +1,154 @@
1
+ // Presentation-axis Reachability & Affordance instrument for e2e-test (issue #197).
2
+ //
3
+ // "Can a user GET to a control and tell it's actionable" — plus the negative form,
4
+ // "things that must be blocked are blocked" (modal focus-trap, no click-through,
5
+ // wrong-role route not reachable). Tag specs `@level presentation:reachability`.
6
+ //
7
+ // Distinct from ensure-coverage's existing Route Reachability Protocol, which is
8
+ // GUARD-CHAIN reachability ("can the URL load past middleware/auth"). This is
9
+ // NAVIGATIONAL reachability ("can a user click their way here from an entry point").
10
+ // A route can be guard-reachable yet navigation-orphaned (nothing links to it).
11
+ //
12
+ // Affordance↔impeccable boundary: the DETERMINISTIC affordance checks live here
13
+ // (focus-indicator presence, ≥44px target size, nav-orphan). Holistic a11y
14
+ // resilience and the *aesthetics* of focus styling stay in the `impeccable` skill
15
+ // (its `harden` reference). This instrument proves the cue EXISTS; impeccable judges
16
+ // whether it's good. See impeccable/reference/harden.md.
17
+
18
+ // ---------- pure cores (no browser) ----------
19
+
20
+ // Navigational orphans = routes the app DEFINES (manifest) minus routes REACHABLE
21
+ // by crawling nav from an entry point. `ignore` suppresses intentionally
22
+ // unlinked-but-valid routes (deep-link targets, auth callbacks) by exact id or
23
+ // prefix ending in '*'.
24
+ export function findNavOrphans(manifestRoutes, reachableRoutes, { ignore = [] } = {}) {
25
+ const reachable = new Set(reachableRoutes)
26
+ const matchesIgnore = (r) => ignore.some((ig) => (ig.endsWith('*') ? r.startsWith(ig.slice(0, -1)) : r === ig))
27
+ return manifestRoutes.filter((r) => !reachable.has(r) && !matchesIgnore(r))
28
+ }
29
+
30
+ // Minimum interactive target size (WCAG 2.5.5 / 2.5.8). Returns pass + which
31
+ // dimension is short. Default 44px (24px is the relaxed AA floor — pass min).
32
+ export function meetsTargetSize({ width = 0, height = 0 } = {}, min = 44) {
33
+ const short = []
34
+ if (width < min) short.push('width')
35
+ if (height < min) short.push('height')
36
+ return { pass: short.length === 0, min, width, height, short }
37
+ }
38
+
39
+ // A focusable control must show a VISIBLE focus indicator: focusing it must change
40
+ // the outline or box-shadow vs its blurred state. Pure comparison of the two
41
+ // computed-style snapshots; absence of any change == no visible focus ring.
42
+ export function hasVisibleFocusIndicator(blurred = {}, focused = {}) {
43
+ const noOutline = (s) =>
44
+ !s.outlineStyle || s.outlineStyle === 'none' || parseFloat(s.outlineWidth || '0') === 0
45
+ // An outline change only counts if the FOCUSED outline is actually visible — a
46
+ // 0px-width color-only change (e.g. `0px solid red` → `0px solid blue`) paints
47
+ // nothing and must not pass.
48
+ const focusedHasOutline = !noOutline(focused)
49
+ const outlineVisibleAndChanged = focusedHasOutline && (
50
+ noOutline(blurred) ||
51
+ (blurred.outlineColor || '') !== (focused.outlineColor || '') ||
52
+ (blurred.outlineWidth || '') !== (focused.outlineWidth || '') ||
53
+ (blurred.outlineStyle || '') !== (focused.outlineStyle || '')
54
+ )
55
+ const shadowChanged = (blurred.boxShadow || 'none') !== (focused.boxShadow || 'none')
56
+ return Boolean(outlineVisibleAndChanged || shadowChanged)
57
+ }
58
+
59
+ // ---------- Playwright wrappers (need a real browser) ----------
60
+
61
+ // BFS-crawl the navigational graph from `start`, clicking same-origin links the
62
+ // rendered nav exposes, and return the set of reachable pathnames. Drives a real
63
+ // browser so JS-rendered / hover/scroll-revealed nav is exercised. Seed an
64
+ // authenticated context for guarded nav.
65
+ export async function crawlReachableRoutes(page, { start = '/', maxPages = 200 } = {}) {
66
+ const origin = new URL(start, page.url() || 'http://localhost').origin
67
+ const seen = new Set()
68
+ const queue = [new URL(start, origin).pathname]
69
+ while (queue.length && seen.size < maxPages) {
70
+ const path = queue.shift()
71
+ if (seen.has(path)) continue
72
+ seen.add(path)
73
+ await page.goto(new URL(path, origin).href).catch(() => {})
74
+ const hrefs = await page.$$eval('a[href]', (as) => as.map((a) => a.getAttribute('href') || ''))
75
+ for (const h of hrefs) {
76
+ let u
77
+ try { u = new URL(h, origin) } catch { continue }
78
+ if (u.origin !== origin) continue
79
+ if (!seen.has(u.pathname)) queue.push(u.pathname)
80
+ }
81
+ }
82
+ return [...seen]
83
+ }
84
+
85
+ // Assert no navigational orphans: every manifest route is reachable by crawling.
86
+ export async function assertNoNavOrphans(page, manifestRoutes, { start = '/', ignore = [] } = {}) {
87
+ const reachable = await crawlReachableRoutes(page, { start })
88
+ const orphans = findNavOrphans(manifestRoutes, reachable, { ignore })
89
+ if (orphans.length) {
90
+ throw new Error(`presentation:reachability — ${orphans.length} route(s) reachable by URL but linked from no nav:\n${orphans.map((o) => ' ' + o).join('\n')}`)
91
+ }
92
+ }
93
+
94
+ // Assert every matched interactive control meets the minimum target size.
95
+ export async function assertTargetSizes(page, selector = 'a, button, [role="button"], input, select', { min = 44 } = {}) {
96
+ const boxes = await page.$$eval(selector, (els) => els
97
+ .filter((el) => el.offsetParent !== null)
98
+ .map((el) => { const r = el.getBoundingClientRect(); return { tag: el.tagName.toLowerCase(), id: el.id, width: r.width, height: r.height } }))
99
+ const small = boxes.map((b) => ({ ...b, ...meetsTargetSize(b, min) })).filter((b) => !b.pass)
100
+ if (small.length) {
101
+ throw new Error(`presentation:reachability — ${small.length} control(s) below ${min}px target:\n${small.map((s) => ` ${s.tag}${s.id ? '#' + s.id : ''} ${Math.round(s.width)}x${Math.round(s.height)}`).join('\n')}`)
102
+ }
103
+ }
104
+
105
+ // Assert a control shows a visible focus indicator when focused.
106
+ export async function assertFocusVisible(page, locator) {
107
+ const read = () => locator.evaluate((el) => {
108
+ const s = getComputedStyle(el)
109
+ return { outlineStyle: s.outlineStyle, outlineWidth: s.outlineWidth, outlineColor: s.outlineColor, boxShadow: s.boxShadow }
110
+ })
111
+ const blurred = await read()
112
+ await locator.focus()
113
+ const focused = await read()
114
+ if (!hasVisibleFocusIndicator(blurred, focused)) {
115
+ throw new Error(`presentation:reachability (affordance) — control shows no visible focus indicator on focus: ${locator}`)
116
+ }
117
+ }
118
+
119
+ // Negative form: a modal must trap focus — tabbing never moves focus to an element
120
+ // outside the modal (no escape to the background). Containment is checked on the
121
+ // modal element the locator resolves to (no fragile re-derived global selector).
122
+ export async function assertModalTrapsFocus(page, modalLocator, { tabs = 12 } = {}) {
123
+ for (let i = 0; i < tabs; i++) {
124
+ await page.keyboard.press('Tab')
125
+ const inside = await modalLocator.evaluate((modal) => modal.contains(document.activeElement))
126
+ if (!inside) {
127
+ throw new Error('presentation:reachability (negative) — focus escaped the modal; focus is not trapped')
128
+ }
129
+ }
130
+ }
131
+
132
+ // Negative form: with a modal open, a background element must not be interactable
133
+ // (no click-through). Asserts the background is `inert`, `aria-hidden`, or covered
134
+ // (pointer-events:none) — the standard ways an open dialog neutralizes the page.
135
+ export async function assertNoBackgroundClickThrough(page, backgroundLocator) {
136
+ const interactable = await backgroundLocator.evaluate((el) => {
137
+ if (el.inert) return false
138
+ if (el.getAttribute('aria-hidden') === 'true') return false
139
+ if (getComputedStyle(el).pointerEvents === 'none') return false
140
+ return true // still interactable behind the modal — a click-through leak
141
+ })
142
+ if (interactable) {
143
+ throw new Error('presentation:reachability (negative) — background is still interactable behind the modal (click-through); expected inert / aria-hidden / pointer-events:none')
144
+ }
145
+ }
146
+
147
+ // Negative form: a privilege-gated route must NOT be navigationally reachable for a
148
+ // user lacking the role (it must not appear in the crawl set).
149
+ export async function assertRouteNotReachable(page, route, { start = '/' } = {}) {
150
+ const reachable = await crawlReachableRoutes(page, { start })
151
+ if (reachable.includes(route)) {
152
+ throw new Error(`presentation:reachability (negative) — privilege-gated route is reachable via nav for the wrong role: ${route}`)
153
+ }
154
+ }
@@ -0,0 +1,69 @@
1
+ // Unit tests for the pure Reachability & Affordance cores (issue #197).
2
+ // Run: `node --test`. The Playwright wrappers (crawlReachableRoutes,
3
+ // assertNoNavOrphans, assertTargetSizes, assertFocusVisible, assertModalTrapsFocus,
4
+ // assertRouteNotReachable) need a real browser and are exercised downstream.
5
+
6
+ import { test } from 'node:test'
7
+ import assert from 'node:assert/strict'
8
+ import { findNavOrphans, meetsTargetSize, hasVisibleFocusIndicator } from './presentation-reachability.mjs'
9
+
10
+ // ---------- nav-orphan set diff ----------
11
+
12
+ test('findNavOrphans: routes in manifest but not reachable are orphans', () => {
13
+ const manifest = ['/', '/dashboard', '/settings', '/secret-report']
14
+ const reachable = ['/', '/dashboard', '/settings']
15
+ assert.deepEqual(findNavOrphans(manifest, reachable), ['/secret-report'])
16
+ })
17
+
18
+ test('findNavOrphans: none when everything is reachable', () => {
19
+ assert.deepEqual(findNavOrphans(['/', '/a'], ['/', '/a', '/extra']), [])
20
+ })
21
+
22
+ test('findNavOrphans: ignore by exact id and by prefix glob', () => {
23
+ const manifest = ['/', '/auth/callback', '/admin/secret', '/deep/x', '/deep/y']
24
+ const reachable = ['/']
25
+ // exact ignore for the callback, prefix ignore for everything under /deep
26
+ const orphans = findNavOrphans(manifest, reachable, { ignore: ['/auth/callback', '/deep/*'] })
27
+ assert.deepEqual(orphans, ['/admin/secret'])
28
+ })
29
+
30
+ // ---------- target size ----------
31
+
32
+ test('meetsTargetSize: 44x44 passes, smaller fails with the short dimension', () => {
33
+ assert.equal(meetsTargetSize({ width: 44, height: 44 }).pass, true)
34
+ const r = meetsTargetSize({ width: 30, height: 44 })
35
+ assert.equal(r.pass, false)
36
+ assert.deepEqual(r.short, ['width'])
37
+ assert.deepEqual(meetsTargetSize({ width: 10, height: 10 }).short, ['width', 'height'])
38
+ })
39
+
40
+ test('meetsTargetSize: custom minimum', () => {
41
+ assert.equal(meetsTargetSize({ width: 24, height: 24 }, 24).pass, true)
42
+ assert.equal(meetsTargetSize({ width: 24, height: 24 }, 44).pass, false)
43
+ })
44
+
45
+ // ---------- focus indicator ----------
46
+
47
+ const NO_OUTLINE = { outlineStyle: 'none', outlineWidth: '0px', outlineColor: 'rgb(0,0,0)', boxShadow: 'none' }
48
+
49
+ test('hasVisibleFocusIndicator: outline appearing on focus → visible', () => {
50
+ const focused = { outlineStyle: 'solid', outlineWidth: '2px', outlineColor: 'rgb(0,90,255)', boxShadow: 'none' }
51
+ assert.equal(hasVisibleFocusIndicator(NO_OUTLINE, focused), true)
52
+ })
53
+
54
+ test('hasVisibleFocusIndicator: box-shadow ring appearing on focus → visible', () => {
55
+ const focused = { ...NO_OUTLINE, boxShadow: '0 0 0 3px rgba(0,90,255,.5)' }
56
+ assert.equal(hasVisibleFocusIndicator(NO_OUTLINE, focused), true)
57
+ })
58
+
59
+ test('hasVisibleFocusIndicator: no change between states → NOT visible (the bug)', () => {
60
+ // outline:none in both states and identical shadow — the missing-focus-ring case
61
+ assert.equal(hasVisibleFocusIndicator(NO_OUTLINE, { ...NO_OUTLINE }), false)
62
+ })
63
+
64
+ test('hasVisibleFocusIndicator: zero-width color-only outline change → NOT visible', () => {
65
+ // `0px solid red` → `0px solid blue` paints nothing; must not false-pass
66
+ const blurred = { outlineStyle: 'solid', outlineWidth: '0px', outlineColor: 'rgb(255,0,0)', boxShadow: 'none' }
67
+ const focused = { outlineStyle: 'solid', outlineWidth: '0px', outlineColor: 'rgb(0,0,255)', boxShadow: 'none' }
68
+ assert.equal(hasVisibleFocusIndicator(blurred, focused), false)
69
+ })
@@ -0,0 +1,141 @@
1
+ // Presentation-axis SPINE instrument for e2e-test: render-health.
2
+ //
3
+ // The cheapest, most foundational presentation check — does the page render
4
+ // without a `pageerror` (uncaught exception), a `console.error`, a rendered error
5
+ // boundary, or an HTTP >= 500? An uncaught TypeError on mount fails here before
6
+ // any layout question is asked. This is the check that catches the class of bug a
7
+ // green functional journey sails past when its only test of the route intercepts
8
+ // the route's own API (the journey never exercises the real render path).
9
+ //
10
+ // Import into a Playwright spec and tag it `@covers <route>` /
11
+ // `@level presentation:render-health` so the Coverage Ledger resolves it. Drives a
12
+ // REAL browser — a DOM-only / JSDOM equivalent cannot observe a pageerror on mount
13
+ // and is graded `Over-mocked` by the ledger. Mirrors the contract in ensure-coverage
14
+ // references/presentation-sweep.md (result shape).
15
+
16
+ // ---- pure, unit-testable core (no browser) ----
17
+
18
+ // Default substrings that mark a console message or error as a framework/error-
19
+ // boundary render failure regardless of source. Repos extend via `extraFatal`.
20
+ export const DEFAULT_FATAL_MARKERS = [
21
+ 'Application error: a client-side exception',
22
+ 'Application error: a server-side exception',
23
+ 'Unhandled Runtime Error',
24
+ 'Cannot read properties of undefined',
25
+ 'Cannot read properties of null',
26
+ 'is not a function',
27
+ 'Hydration failed',
28
+ 'There was an error while hydrating',
29
+ 'Minified React error',
30
+ ]
31
+
32
+ // Decide whether a single captured console message is fatal. `type` is the console
33
+ // level ('error' | 'warning' | …). console.error is fatal by default; warnings are
34
+ // not. `ignore` suppresses known-benign messages by substring (curate sparingly).
35
+ export function isFatalConsole(type, text, { ignore = [] } = {}) {
36
+ if (type !== 'error') return false
37
+ if (ignore.some((ig) => text.includes(ig))) return false
38
+ return true
39
+ }
40
+
41
+ // A response is fatal if the document or any first-party request returns >= 500.
42
+ // Cross-origin (external) responses are ignored — externals are faked, not graded.
43
+ export function isFatalResponse(status, url, { origin } = {}) {
44
+ if (status < 500) return false
45
+ if (origin && !url.startsWith(origin) && !url.startsWith('/')) return false
46
+ return true
47
+ }
48
+
49
+ // Does any captured text match an error-boundary / framework-error marker?
50
+ export function hasFatalMarker(text, { extraFatal = [] } = {}) {
51
+ const markers = [...DEFAULT_FATAL_MARKERS, ...extraFatal]
52
+ return markers.some((m) => text.includes(m))
53
+ }
54
+
55
+ // Reduce a batch of captured signals into a verdict. Pure — the Playwright wrapper
56
+ // collects `signals` and delegates here so the decision logic is testable headless.
57
+ // signals: { pageerrors:[{message}], consoles:[{type,text}], responses:[{status,url}], bodyText? }
58
+ export function evaluateRenderHealth(signals = {}, opts = {}) {
59
+ const { ignore = [], extraFatal = [], origin } = opts
60
+ const offenders = []
61
+ for (const e of signals.pageerrors || []) {
62
+ offenders.push({ kind: 'pageerror', detail: e.message || String(e) })
63
+ }
64
+ for (const c of signals.consoles || []) {
65
+ if (isFatalConsole(c.type, c.text || '', { ignore })) {
66
+ offenders.push({ kind: 'console.error', detail: c.text })
67
+ }
68
+ }
69
+ for (const r of signals.responses || []) {
70
+ if (isFatalResponse(r.status, r.url || '', { origin })) {
71
+ offenders.push({ kind: 'http>=500', detail: `${r.status} ${r.url}` })
72
+ }
73
+ }
74
+ if (signals.bodyText && hasFatalMarker(signals.bodyText, { extraFatal })) {
75
+ offenders.push({ kind: 'error-boundary', detail: 'error overlay / boundary text present in DOM' })
76
+ }
77
+ return { healthy: offenders.length === 0, offenders }
78
+ }
79
+
80
+ // ---- browser wrapper (Playwright) ----
81
+
82
+ // Attach listeners BEFORE navigation, then drive: returns a handle whose
83
+ // `.assert()` throws with the concrete offender(s) if the page did not render
84
+ // healthy. Usage:
85
+ // const health = watchRenderHealth(page, { origin: baseURL })
86
+ // await page.goto('/tasks')
87
+ // await page.waitForLoadState('networkidle').catch(() => {})
88
+ // await health.assert()
89
+ export function watchRenderHealth(page, opts = {}) {
90
+ const signals = { pageerrors: [], consoles: [], responses: [] }
91
+ page.on('pageerror', (err) => signals.pageerrors.push({ message: err?.message || String(err) }))
92
+ page.on('console', (msg) => signals.consoles.push({ type: msg.type(), text: msg.text() }))
93
+ page.on('response', (res) => signals.responses.push({ status: res.status(), url: res.url() }))
94
+ return {
95
+ signals,
96
+ async evaluate() {
97
+ // Pull rendered body text so an error boundary that swallowed the throw is caught too.
98
+ try { signals.bodyText = await page.evaluate(() => document.body?.innerText || '') } catch { /* page gone */ }
99
+ return evaluateRenderHealth(signals, opts)
100
+ },
101
+ async assert() {
102
+ const { healthy, offenders } = await this.evaluate()
103
+ if (!healthy) {
104
+ const lines = offenders.map((o) => ` - [${o.kind}] ${o.detail}`).join('\n')
105
+ throw new Error(`presentation:render-health — page did not render cleanly:\n${lines}`)
106
+ }
107
+ },
108
+ }
109
+ }
110
+
111
+ // Continuous-invariant form: render-health must hold not only at LOAD but THROUGH an
112
+ // interaction — a `presentation:temporal` trigger, a `presentation:perceivability`
113
+ // post-delete mutation, or a mutation-form journey's submit. Attach the watcher,
114
+ // run the interaction, then assert nothing threw during it. COMPOSE with the other
115
+ // presentation instruments rather than re-driving the form:
116
+ //
117
+ // await assertHealthyThrough(page, async () => {
118
+ // await assertRemovedAfterMutation(page, page.getByRole('row', { name: 'X' }))
119
+ // }, { origin: baseURL })
120
+ //
121
+ // This is "mutate-health": render-health × the trigger/mutation pattern temporal and
122
+ // perceivability already use. The durable-state assertion (depth) and the visible
123
+ // post-state assertion (perceivability) stay as-is; this adds "…and nothing threw
124
+ // while it happened." Reuse one handle across several interactions for full lifecycle
125
+ // coverage, or call this per interaction.
126
+ export async function assertHealthyThrough(page, action, opts = {}) {
127
+ const health = watchRenderHealth(page, opts)
128
+ await action()
129
+ await health.assert()
130
+ }
131
+
132
+ // One-shot convenience: navigate and assert render-health in a single call.
133
+ export async function assertRenderHealthy(page, url, opts = {}) {
134
+ const health = watchRenderHealth(page, opts)
135
+ const res = await page.goto(url, { waitUntil: 'domcontentloaded' })
136
+ await page.waitForLoadState('networkidle').catch(() => {})
137
+ if (res && isFatalResponse(res.status(), res.url(), { origin: opts.origin })) {
138
+ throw new Error(`presentation:render-health — document returned ${res.status()} for ${url}`)
139
+ }
140
+ await health.assert()
141
+ }
@@ -0,0 +1,98 @@
1
+ // Unit tests for the render-health spine — the pure core, no browser.
2
+ import { test } from 'node:test'
3
+ import assert from 'node:assert/strict'
4
+
5
+ import {
6
+ isFatalConsole,
7
+ isFatalResponse,
8
+ hasFatalMarker,
9
+ evaluateRenderHealth,
10
+ assertHealthyThrough,
11
+ } from './presentation-render-health.mjs'
12
+
13
+ // Minimal fake Playwright page: records listeners, lets a test emit events, and
14
+ // returns body text from evaluate(). Enough to exercise the watcher/compose path
15
+ // without a browser.
16
+ function fakePage(bodyText = '') {
17
+ const handlers = {}
18
+ return {
19
+ on(ev, fn) { (handlers[ev] ||= []).push(fn) },
20
+ emit(ev, arg) { (handlers[ev] || []).forEach((f) => f(arg)) },
21
+ async evaluate() { return bodyText },
22
+ }
23
+ }
24
+
25
+ test('console.error is fatal; warning is not', () => {
26
+ assert.equal(isFatalConsole('error', 'boom'), true)
27
+ assert.equal(isFatalConsole('warning', 'meh'), false)
28
+ assert.equal(isFatalConsole('log', 'hi'), false)
29
+ })
30
+
31
+ test('ignored console substrings are not fatal', () => {
32
+ assert.equal(isFatalConsole('error', 'Download the React DevTools', { ignore: ['React DevTools'] }), false)
33
+ })
34
+
35
+ test('http >= 500 is fatal; < 500 is not', () => {
36
+ assert.equal(isFatalResponse(500, '/api/x'), true)
37
+ assert.equal(isFatalResponse(503, '/api/x'), true)
38
+ assert.equal(isFatalResponse(404, '/api/x'), false)
39
+ assert.equal(isFatalResponse(200, '/api/x'), false)
40
+ })
41
+
42
+ test('cross-origin 5xx is ignored (externals are faked, not graded)', () => {
43
+ assert.equal(isFatalResponse(500, 'https://stripe.com/v1/x', { origin: 'http://localhost:3000' }), false)
44
+ assert.equal(isFatalResponse(500, 'http://localhost:3000/api/x', { origin: 'http://localhost:3000' }), true)
45
+ assert.equal(isFatalResponse(500, '/api/x', { origin: 'http://localhost:3000' }), true) // relative = first-party
46
+ })
47
+
48
+ test('error-boundary markers are detected in body text', () => {
49
+ assert.equal(hasFatalMarker('Application error: a client-side exception has occurred'), true)
50
+ assert.equal(hasFatalMarker("Cannot read properties of undefined (reading 'id')"), true)
51
+ assert.equal(hasFatalMarker('All good here'), false)
52
+ })
53
+
54
+ test('the /tasks bug shape fails render-health (pageerror on mount)', () => {
55
+ const v = evaluateRenderHealth({
56
+ pageerrors: [{ message: "Cannot read properties of undefined (reading 'id')" }],
57
+ })
58
+ assert.equal(v.healthy, false)
59
+ assert.equal(v.offenders[0].kind, 'pageerror')
60
+ assert.match(v.offenders[0].detail, /reading 'id'/)
61
+ })
62
+
63
+ test('a clean render is healthy', () => {
64
+ const v = evaluateRenderHealth({
65
+ consoles: [{ type: 'log', text: 'mounted' }, { type: 'warning', text: 'deprecated' }],
66
+ responses: [{ status: 200, url: '/api/tasks/list' }],
67
+ bodyText: 'Tasks\nNo tasks match these filters.',
68
+ })
69
+ assert.equal(v.healthy, true)
70
+ assert.deepEqual(v.offenders, [])
71
+ })
72
+
73
+ test('assertHealthyThrough fails when an error fires DURING the interaction (mutate-health)', async () => {
74
+ const page = fakePage()
75
+ await assert.rejects(
76
+ assertHealthyThrough(page, async () => {
77
+ // simulate an uncaught error thrown while a delete/submit runs
78
+ page.emit('pageerror', { message: "Cannot read properties of undefined (reading 'id')" })
79
+ }),
80
+ /render-health.*reading 'id'/s,
81
+ )
82
+ })
83
+
84
+ test('assertHealthyThrough passes a clean interaction', async () => {
85
+ const page = fakePage('Tasks\nDeleted.')
86
+ await assertHealthyThrough(page, async () => { /* clean mutation */ })
87
+ })
88
+
89
+ test('aggregates multiple offenders (pageerror + console + 5xx + boundary)', () => {
90
+ const v = evaluateRenderHealth({
91
+ pageerrors: [{ message: 'x is not a function' }],
92
+ consoles: [{ type: 'error', text: 'render failed' }],
93
+ responses: [{ status: 500, url: '/api/tasks/list' }],
94
+ bodyText: 'Unhandled Runtime Error',
95
+ }, { origin: 'http://localhost:3000' })
96
+ assert.equal(v.healthy, false)
97
+ assert.deepEqual(v.offenders.map((o) => o.kind).sort(), ['console.error', 'error-boundary', 'http>=500', 'pageerror'])
98
+ })