@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,200 @@
1
+ #!/usr/bin/env node
2
+
3
+ /*
4
+ detect-routes-nextjs.mjs
5
+
6
+ Detect all Next.js App Router routes and compare against route manifest.
7
+ Fails CI if any route exists in the app but not in the manifest.
8
+
9
+ Scans for page routes (page.tsx) and API routes (route.ts).
10
+
11
+ Handles Next.js conventions:
12
+ Route groups (auth)/ -- stripped from path
13
+ Dynamic segments [id]/ -- preserved
14
+ Catch-all [...slug]/ -- preserved
15
+ Parallel routes @panel/ -- skipped
16
+ Intercepting routes (.)/ -- skipped
17
+ Private folders _components/ -- skipped
18
+
19
+ Usage:
20
+ node detect-routes-nextjs.mjs [options]
21
+
22
+ Options:
23
+ --app-dir <path> Path to app/ directory (default: src/app or app)
24
+ --manifest <path> Path to route-manifest.json (default: e2e/route-manifest.json)
25
+ --strict Exit 1 if any route is missing (default: true)
26
+ --no-strict Only warn, don't fail
27
+
28
+ Exit codes:
29
+ 0 -- all routes accounted for
30
+ 1 -- routes missing from manifest (strict mode)
31
+ */
32
+
33
+ import { readdirSync, existsSync, readFileSync, statSync } from 'fs';
34
+ import { join, resolve, relative, sep } from 'path';
35
+
36
+ // ─── CLI args ───────────────────────────────────────────────
37
+ const args = process.argv.slice(2);
38
+ function getArg(name, defaultVal) {
39
+ const idx = args.indexOf(`--${name}`);
40
+ return idx >= 0 && args[idx + 1] ? args[idx + 1] : defaultVal;
41
+ }
42
+ const hasFlag = (name) => args.includes(`--${name}`);
43
+ const strict = !hasFlag('no-strict');
44
+
45
+ // Auto-detect app directory
46
+ function findAppDir() {
47
+ const explicit = getArg('app-dir', '');
48
+ if (explicit) return resolve(explicit);
49
+ if (existsSync('src/app')) return resolve('src/app');
50
+ if (existsSync('app')) return resolve('app');
51
+ return null;
52
+ }
53
+
54
+ const appDir = findAppDir();
55
+ const manifestPath = resolve(getArg('manifest', 'e2e/route-manifest.json'));
56
+
57
+ if (!appDir || !existsSync(appDir)) {
58
+ console.error(`❌ App directory not found. Use --app-dir <path>`);
59
+ process.exit(1);
60
+ }
61
+
62
+ // ─── Route scanning ─────────────────────────────────────────
63
+
64
+ /**
65
+ * Recursively scan the app directory for page.tsx/ts and route.ts/tsx files.
66
+ * Returns an array of { path: string, type: 'page' | 'api', file: string }
67
+ */
68
+ function scanRoutes(dir, segments = []) {
69
+ const routes = [];
70
+ let entries;
71
+ try {
72
+ entries = readdirSync(dir, { withFileTypes: true });
73
+ } catch {
74
+ return routes;
75
+ }
76
+
77
+ // Check for route files in current directory
78
+ const pageFile = entries.find(
79
+ (e) => e.isFile() && /^page\.(tsx?|jsx?)$/.test(e.name)
80
+ );
81
+ const routeFile = entries.find(
82
+ (e) => e.isFile() && /^route\.(tsx?|jsx?)$/.test(e.name)
83
+ );
84
+
85
+ if (pageFile) {
86
+ const routePath = '/' + segments.join('/') || '/';
87
+ routes.push({
88
+ path: routePath === '/' ? '/' : routePath,
89
+ type: 'page',
90
+ file: relative(process.cwd(), join(dir, pageFile.name)),
91
+ });
92
+ }
93
+
94
+ if (routeFile) {
95
+ const routePath = '/' + segments.join('/') || '/';
96
+ routes.push({
97
+ path: routePath,
98
+ type: 'api',
99
+ file: relative(process.cwd(), join(dir, routeFile.name)),
100
+ });
101
+ }
102
+
103
+ // Recurse into subdirectories
104
+ for (const entry of entries) {
105
+ if (!entry.isDirectory()) continue;
106
+ const name = entry.name;
107
+
108
+ // Skip private folders (Next.js convention: _name)
109
+ if (name.startsWith('_')) continue;
110
+
111
+ // Skip parallel routes (@panel, @modal — not independent routes)
112
+ if (name.startsWith('@')) continue;
113
+
114
+ // Skip intercepting routes ((.), (..), (..)(..))
115
+ if (/^\(\.+\)/.test(name)) continue;
116
+
117
+ // Route groups: (groupName) => strip from path
118
+ if (/^\([^)]+\)$/.test(name) && !name.startsWith('(.)')) {
119
+ routes.push(...scanRoutes(join(dir, name), segments));
120
+ continue;
121
+ }
122
+
123
+ // Normal segment (including [dynamic], [...catchAll], [[...optional]])
124
+ routes.push(...scanRoutes(join(dir, name), [...segments, name]));
125
+ }
126
+
127
+ return routes;
128
+ }
129
+
130
+ // ─── Manifest comparison ────────────────────────────────────
131
+
132
+ // Load manifest
133
+ let manifest = { routes: [] };
134
+ if (existsSync(manifestPath)) {
135
+ try {
136
+ manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
137
+ } catch (e) {
138
+ console.error(`❌ Invalid JSON in manifest: ${e.message}`);
139
+ process.exit(1);
140
+ }
141
+ } else {
142
+ console.error(`❌ Manifest not found: ${manifestPath}`);
143
+ console.error(` Create it with at minimum: { "version": 1, "routes": [] }`);
144
+ process.exit(1);
145
+ }
146
+
147
+ const manifestRoutes = new Set(
148
+ (manifest.routes || []).map((r) => r.path)
149
+ );
150
+ // Also check endpoints (for API routes in combined manifests)
151
+ const manifestEndpoints = new Set(
152
+ (manifest.endpoints || []).map((r) => r.path)
153
+ );
154
+ const allManifestPaths = new Set([...manifestRoutes, ...manifestEndpoints]);
155
+
156
+ // Scan
157
+ console.log(`🔍 Scanning: ${relative(process.cwd(), appDir)}/`);
158
+ console.log(`📋 Manifest: ${relative(process.cwd(), manifestPath)}\n`);
159
+
160
+ const appRoutes = scanRoutes(appDir);
161
+ const missing = [];
162
+ const accounted = [];
163
+
164
+ for (const route of appRoutes) {
165
+ if (allManifestPaths.has(route.path)) {
166
+ accounted.push(route);
167
+ } else {
168
+ missing.push(route);
169
+ }
170
+ }
171
+
172
+ // ─── Report ─────────────────────────────────────────────────
173
+
174
+ console.log(`Found ${appRoutes.length} routes in app (${appRoutes.filter(r => r.type === 'page').length} pages, ${appRoutes.filter(r => r.type === 'api').length} API routes)`);
175
+ console.log(`Manifest has ${allManifestPaths.size} entries`);
176
+ console.log(`Accounted: ${accounted.length} | Missing: ${missing.length}\n`);
177
+
178
+ if (missing.length > 0) {
179
+ console.log(`${'─'.repeat(60)}`);
180
+ console.log(`❌ Routes in app but NOT in manifest:\n`);
181
+ for (const route of missing) {
182
+ const tag = route.type === 'api' ? ' [API]' : '';
183
+ console.log(` ${route.path}${tag}`);
184
+ console.log(` └─ ${route.file}`);
185
+ }
186
+ console.log(`\n${'─'.repeat(60)}`);
187
+ console.log(`\nAdd these to ${relative(process.cwd(), manifestPath)}:`);
188
+ console.log(` { "path": "<route>", "tier": 1, "specs": [] }\n`);
189
+ console.log(`Routes with specs: [] will pass CI but show as uncovered`);
190
+ console.log(`in the E2E PR comment.\n`);
191
+
192
+ if (strict) {
193
+ console.log('🚫 Failing CI (strict mode). Use --no-strict to warn only.');
194
+ process.exit(1);
195
+ } else {
196
+ console.log('⚠️ Warning only (non-strict mode).');
197
+ }
198
+ } else {
199
+ console.log('✅ All app routes are accounted for in the manifest.');
200
+ }
@@ -0,0 +1,171 @@
1
+ // Presentation-axis instruments for e2e-test (issue #195).
2
+ //
3
+ // Real-rendered-page checks — import into a Playwright spec and tag it
4
+ // `@covers <route>` / `@level presentation:spatial` so the Coverage Ledger
5
+ // resolves it. The spine ships the Spatial/overflow invariant; later slices add
6
+ // perceivability (contrast), reachability, and temporal (layout shift).
7
+ //
8
+ // These drive a REAL browser. A DOM-only / JSDOM equivalent cannot see layout and
9
+ // is graded `Over-mocked` by the ledger — that is intentional.
10
+
11
+ // The condition-matrix floor: every browser route is checked at these viewports.
12
+ // Mirrors PRESENTATION_VIEWPORT_FLOOR in ensure-coverage's coverage-ledger.mjs.
13
+ export const PRESENTATION_VIEWPORT_FLOOR = [
14
+ { name: 'mobile', width: 375, height: 812 },
15
+ { name: 'desktop', width: 1280, height: 800 },
16
+ ]
17
+
18
+ // Pure predicate (no browser) — given element geometry and the viewport width,
19
+ // return the elements that overflow horizontally. Extracted so the detection
20
+ // logic is unit-testable without a browser; the Playwright wrapper collects the
21
+ // geometry and delegates here.
22
+ //
23
+ // elements: [{ selector, left, right, width, height }]
24
+ export function findHorizontalOverflow(elements, viewportWidth, { tolerancePx = 1, ignore = [] } = {}) {
25
+ const offenders = []
26
+ for (const el of elements) {
27
+ if (!el.width || !el.height) continue // not rendered
28
+ const overflowsRight = el.right - tolerancePx > viewportWidth
29
+ const overflowsLeft = el.left < -tolerancePx
30
+ if (!overflowsRight && !overflowsLeft) continue
31
+ if (ignore.some((ig) => (el.selector || '').includes(ig))) continue
32
+ offenders.push({
33
+ selector: el.selector,
34
+ overflowPx: Math.round(overflowsRight ? el.right - viewportWidth : -el.left),
35
+ })
36
+ }
37
+ return offenders
38
+ }
39
+
40
+ // In-browser collector, serialized into page.evaluate. Returns the viewport width
41
+ // and one record per body descendant.
42
+ function collectGeometry() {
43
+ const vw = document.documentElement.clientWidth
44
+ const sel = (el) => {
45
+ if (el.id) return '#' + el.id
46
+ let s = el.tagName.toLowerCase()
47
+ if (typeof el.className === 'string' && el.className.trim()) {
48
+ s += '.' + el.className.trim().split(/\s+/).join('.')
49
+ }
50
+ return s
51
+ }
52
+ const elements = [...document.querySelectorAll('body *')].map((el) => {
53
+ const r = el.getBoundingClientRect()
54
+ return { selector: sel(el), left: r.left, right: r.right, width: r.width, height: r.height }
55
+ })
56
+ return { vw, elements }
57
+ }
58
+
59
+ // Assert no unintended horizontal overflow at each floor viewport. Throws with the
60
+ // offending selectors + overflow amounts. `ignore` suppresses intentional cases by
61
+ // substring match on the selector (curate sparingly).
62
+ export async function assertNoHorizontalOverflow(page, { viewports = PRESENTATION_VIEWPORT_FLOOR, tolerancePx = 1, ignore = [] } = {}) {
63
+ const offenders = []
64
+ for (const vp of viewports) {
65
+ await page.setViewportSize({ width: vp.width, height: vp.height })
66
+ // Let layout settle (fonts/images) — real condition, not a fixed sleep.
67
+ await page.waitForLoadState('networkidle').catch(() => {})
68
+ const { vw, elements } = await page.evaluate(collectGeometry)
69
+ for (const o of findHorizontalOverflow(elements, vw, { tolerancePx, ignore })) {
70
+ offenders.push({ viewport: vp.name, width: vp.width, ...o })
71
+ }
72
+ }
73
+ if (offenders.length) {
74
+ const lines = offenders.map((o) => ` [${o.viewport} ${o.width}px] ${o.selector} overflows by ${o.overflowPx}px`)
75
+ throw new Error(`presentation:spatial — horizontal overflow detected:\n${lines.join('\n')}`)
76
+ }
77
+ }
78
+
79
+ // ---------- overlap detection (part of presentation:spatial) ----------
80
+
81
+ // Pure AABB intersection test with a tolerance (sub-pixel touching is not overlap).
82
+ export function rectsIntersect(a, b, tolerancePx = 1) {
83
+ return (
84
+ a.left < b.right - tolerancePx &&
85
+ a.right > b.left + tolerancePx &&
86
+ a.top < b.bottom - tolerancePx &&
87
+ a.bottom > b.top + tolerancePx
88
+ )
89
+ }
90
+
91
+ // Pure: find intersecting element pairs. `elements` is a flat list of rendered
92
+ // elements with geometry + an `interactive` flag; only sibling-ish *content*
93
+ // overlaps matter, so a pair where one element CONTAINS the other (nesting) is
94
+ // skipped — nesting is normal, collision is not.
95
+ function contains(outer, inner, tol = 1) {
96
+ return outer.left - tol <= inner.left && outer.right + tol >= inner.right &&
97
+ outer.top - tol <= inner.top && outer.bottom + tol >= inner.bottom
98
+ }
99
+ export function findOverlaps(elements, { tolerancePx = 1 } = {}) {
100
+ const els = elements.filter((e) => (e.rect?.width || 0) > 0 && (e.rect?.height || 0) > 0)
101
+ const pairs = []
102
+ for (let i = 0; i < els.length; i++) {
103
+ for (let j = i + 1; j < els.length; j++) {
104
+ const a = els[i], b = els[j]
105
+ if (!rectsIntersect(a.rect, b.rect, tolerancePx)) continue
106
+ if (contains(a.rect, b.rect, tolerancePx) || contains(b.rect, a.rect, tolerancePx)) continue // nesting
107
+ pairs.push({ a: a.selector, b: b.selector, aInteractive: !!a.interactive, bInteractive: !!b.interactive })
108
+ }
109
+ }
110
+ return pairs
111
+ }
112
+
113
+ // Pure: apply the GATED allowlist to overlap pairs. An allowlist entry needs a
114
+ // `match` (substring tested against either selector), a `category`, and a `reason`.
115
+ // GATE (mirrors the Surface Baseline Contract's exclusion gate): an entry may NOT
116
+ // suppress a pair where an **interactive** element is occluded — you cannot
117
+ // allowlist a reachable control into invisibility. Returns reported (still failing),
118
+ // suppressed (census), and rejected (bad allowlist usage — also failing).
119
+ export function applyOverlapAllowlist(pairs, allowlist = []) {
120
+ const reported = [], suppressed = [], rejected = []
121
+ for (const p of pairs) {
122
+ const entry = allowlist.find((e) => e.match && (p.a.includes(e.match) || p.b.includes(e.match)))
123
+ if (!entry) { reported.push(p); continue }
124
+ if (!entry.category || !entry.reason) { rejected.push({ ...p, why: 'allowlist entry needs category + reason' }); continue }
125
+ if (p.aInteractive || p.bInteractive) { rejected.push({ ...p, why: `cannot allowlist an overlap that occludes an interactive element (${entry.category})` }); continue }
126
+ suppressed.push({ ...p, category: entry.category, reason: entry.reason })
127
+ }
128
+ return { reported, suppressed, rejected }
129
+ }
130
+
131
+ // In-browser collector for overlap: geometry + interactivity per element.
132
+ function collectOverlapGeometry() {
133
+ const sel = (el) => {
134
+ if (el.id) return '#' + el.id
135
+ let s = el.tagName.toLowerCase()
136
+ if (typeof el.className === 'string' && el.className.trim()) s += '.' + el.className.trim().split(/\s+/).join('.')
137
+ return s
138
+ }
139
+ const INTERACTIVE = 'a,button,input,select,textarea,[role="button"],[role="link"],[tabindex]'
140
+ return [...document.querySelectorAll('body *')].map((el) => {
141
+ const r = el.getBoundingClientRect()
142
+ return {
143
+ selector: sel(el),
144
+ rect: { left: r.left, top: r.top, right: r.right, bottom: r.bottom, width: r.width, height: r.height },
145
+ interactive: el.matches(INTERACTIVE),
146
+ }
147
+ })
148
+ }
149
+
150
+ // Assert no unintended element overlap. Intentional z-stacks (carousels, badges,
151
+ // sticky headers) are suppressed only via the gated `allowlist`
152
+ // (`[{ match, category, reason }]`); the suppressions are returned as a visible
153
+ // census. Throws on real overlaps and on rejected (ungated/interactive) allowlist use.
154
+ export async function assertNoOverlap(page, { selector, tolerancePx = 1, allowlist = [], viewports = PRESENTATION_VIEWPORT_FLOOR } = {}) {
155
+ const failures = [], census = []
156
+ for (const vp of viewports) {
157
+ await page.setViewportSize({ width: vp.width, height: vp.height })
158
+ await page.waitForLoadState('networkidle').catch(() => {})
159
+ let elements = await page.evaluate(collectOverlapGeometry)
160
+ if (selector) elements = elements.filter((e) => e.selector.includes(selector))
161
+ const pairs = findOverlaps(elements, { tolerancePx })
162
+ const { reported, suppressed, rejected } = applyOverlapAllowlist(pairs, allowlist)
163
+ for (const r of [...reported, ...rejected]) failures.push({ viewport: vp.name, ...r })
164
+ for (const s of suppressed) census.push({ viewport: vp.name, ...s })
165
+ }
166
+ if (failures.length) {
167
+ const lines = failures.map((f) => ` [${f.viewport}] ${f.a} ⨯ ${f.b}${f.why ? ` — ${f.why}` : ''}`)
168
+ throw new Error(`presentation:spatial — element overlap detected:\n${lines.join('\n')}`)
169
+ }
170
+ return { census } // visible record of intentional, gated suppressions
171
+ }
@@ -0,0 +1,106 @@
1
+ // Unit tests for the pure overflow predicate (issue #195). Run: `node --test`.
2
+ // The Playwright wrapper (assertNoHorizontalOverflow) needs a real browser and is
3
+ // exercised downstream; this proves the detection logic without one.
4
+
5
+ import { test } from 'node:test'
6
+ import assert from 'node:assert/strict'
7
+ import {
8
+ findHorizontalOverflow, PRESENTATION_VIEWPORT_FLOOR,
9
+ rectsIntersect, findOverlaps, applyOverlapAllowlist,
10
+ } from './presentation-checks.mjs'
11
+
12
+ // helper: build an element with a rect + interactive flag
13
+ const el = (selector, [left, top, right, bottom], interactive = false) =>
14
+ ({ selector, rect: { left, top, right, bottom, width: right - left, height: bottom - top }, interactive })
15
+
16
+ const VW = 375
17
+
18
+ test('no overflow when everything fits', () => {
19
+ const els = [
20
+ { selector: 'header', left: 0, right: 375, width: 375, height: 60 },
21
+ { selector: '.card', left: 16, right: 359, width: 343, height: 120 },
22
+ ]
23
+ assert.deepEqual(findHorizontalOverflow(els, VW), [])
24
+ })
25
+
26
+ test('flags an element wider than the viewport (right overflow)', () => {
27
+ const els = [{ selector: '.wide-table', left: 0, right: 520, width: 520, height: 200 }]
28
+ const out = findHorizontalOverflow(els, VW)
29
+ assert.equal(out.length, 1)
30
+ assert.equal(out[0].selector, '.wide-table')
31
+ assert.equal(out[0].overflowPx, 145) // 520 - 375
32
+ })
33
+
34
+ test('flags a negatively-positioned element (left overflow)', () => {
35
+ const els = [{ selector: '#drawer', left: -120, right: 200, width: 320, height: 400 }]
36
+ const out = findHorizontalOverflow(els, VW)
37
+ assert.equal(out.length, 1)
38
+ assert.equal(out[0].overflowPx, 120)
39
+ })
40
+
41
+ test('respects the tolerance band (sub-pixel rounding does not flag)', () => {
42
+ const els = [{ selector: '.x', left: 0, right: 375.5, width: 375.5, height: 10 }]
43
+ assert.deepEqual(findHorizontalOverflow(els, VW, { tolerancePx: 1 }), [])
44
+ })
45
+
46
+ test('ignore list suppresses intentional overflow by selector substring', () => {
47
+ const els = [{ selector: '.carousel-track', left: 0, right: 900, width: 900, height: 150 }]
48
+ assert.deepEqual(findHorizontalOverflow(els, VW, { ignore: ['carousel'] }), [])
49
+ })
50
+
51
+ test('zero-size (unrendered) elements are skipped', () => {
52
+ const els = [{ selector: '.hidden', left: 0, right: 999, width: 0, height: 0 }]
53
+ assert.deepEqual(findHorizontalOverflow(els, VW), [])
54
+ })
55
+
56
+ test('viewport floor is the documented 375 + 1280 pair', () => {
57
+ assert.deepEqual(PRESENTATION_VIEWPORT_FLOOR.map((v) => v.width), [375, 1280])
58
+ })
59
+
60
+ // ---------- overlap detection (presentation:spatial) ----------
61
+
62
+ test('rectsIntersect: overlapping yes, separated no, touching (within tol) no', () => {
63
+ const a = { left: 0, top: 0, right: 100, bottom: 50 }
64
+ assert.equal(rectsIntersect(a, { left: 50, top: 25, right: 150, bottom: 75 }), true)
65
+ assert.equal(rectsIntersect(a, { left: 200, top: 0, right: 300, bottom: 50 }), false)
66
+ assert.equal(rectsIntersect(a, { left: 100, top: 0, right: 200, bottom: 50 }), false) // edge-touch
67
+ })
68
+
69
+ test('findOverlaps: flags a collision but skips pure nesting', () => {
70
+ const parent = el('.card', [0, 0, 200, 200])
71
+ const child = el('.card .title', [10, 10, 190, 40]) // nested inside parent
72
+ const stray = el('.badge', [180, 180, 260, 260]) // overlaps parent corner
73
+ const pairs = findOverlaps([parent, child, stray])
74
+ // parent⨯child is nesting (skipped); parent⨯stray is a real overlap
75
+ assert.equal(pairs.length, 1)
76
+ assert.equal(pairs[0].b, '.badge')
77
+ })
78
+
79
+ test('applyOverlapAllowlist: gated suppression yields a census entry', () => {
80
+ const pairs = [{ a: '.carousel-track', b: '.carousel-slide', aInteractive: false, bInteractive: false }]
81
+ const r = applyOverlapAllowlist(pairs, [{ match: 'carousel', category: 'decorative', reason: 'z-stacked slides' }])
82
+ assert.equal(r.reported.length, 0)
83
+ assert.equal(r.suppressed.length, 1)
84
+ assert.equal(r.suppressed[0].reason, 'z-stacked slides')
85
+ })
86
+
87
+ test('applyOverlapAllowlist: entry without category/reason is rejected (still fails)', () => {
88
+ const pairs = [{ a: '.x', b: '.y', aInteractive: false, bInteractive: false }]
89
+ const r = applyOverlapAllowlist(pairs, [{ match: '.x' }])
90
+ assert.equal(r.suppressed.length, 0)
91
+ assert.equal(r.rejected.length, 1)
92
+ })
93
+
94
+ test('applyOverlapAllowlist: cannot allowlist an overlap that occludes an interactive element', () => {
95
+ const pairs = [{ a: '.overlay', b: 'button#save', aInteractive: false, bInteractive: true }]
96
+ const r = applyOverlapAllowlist(pairs, [{ match: '.overlay', category: 'decorative', reason: 'hero flourish' }])
97
+ assert.equal(r.suppressed.length, 0)
98
+ assert.equal(r.rejected.length, 1)
99
+ assert.match(r.rejected[0].why, /interactive/)
100
+ })
101
+
102
+ test('applyOverlapAllowlist: unmatched overlap is reported', () => {
103
+ const pairs = [{ a: '.a', b: '.b', aInteractive: false, bInteractive: false }]
104
+ const r = applyOverlapAllowlist(pairs, [{ match: '.zzz', category: 'decorative', reason: 'n/a' }])
105
+ assert.equal(r.reported.length, 1)
106
+ })
@@ -0,0 +1,179 @@
1
+ // Presentation-axis Perceivability instrument for e2e-test (issue #196).
2
+ //
3
+ // "Can a human actually see/distinguish this control" — and its negative form,
4
+ // "is something that must be hidden actually gone" (privilege leak, stale item).
5
+ //
6
+ // Positive form uses axe-core's `color-contrast` rule against the REAL rendered
7
+ // page. Tag the spec `@covers <route>` / `@level presentation:perceivability`.
8
+ // A DOM-only / JSDOM equivalent is graded Over-mocked by the ledger.
9
+ //
10
+ // Abstain doctrine: axe abstains ("incomplete") on text over a background-image /
11
+ // gradient / partially-obscured element — exactly the case that hides an invisible
12
+ // control. An abstain is NOT a silent pass: it surfaces as a blocking AbstainError.
13
+ // Resolve it by a manual check (verified: true) OR by
14
+ // escalating to a stronger instrument (screenshot / AI-vision triage). AI-vision is
15
+ // triage-only — it may flag for human attestation but can never by itself promote
16
+ // an obligation to Covered (run-to-run non-determinism).
17
+
18
+ // Distinguishable error so callers/CI can tell an abstain from a hard violation.
19
+ export class AbstainError extends Error {
20
+ constructor(message, details) {
21
+ super(message)
22
+ this.name = 'AbstainError'
23
+ this.abstain = true
24
+ this.details = details
25
+ }
26
+ }
27
+
28
+ // ---------- pure WCAG contrast math (testable, no browser) ----------
29
+
30
+ // sRGB channel [0..255] → linearized component for relative luminance.
31
+ function linearize(c) {
32
+ const s = c / 255
33
+ return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4
34
+ }
35
+
36
+ // WCAG relative luminance of an [r,g,b] triple.
37
+ export function relativeLuminance([r, g, b]) {
38
+ return 0.2126 * linearize(r) + 0.7152 * linearize(g) + 0.0722 * linearize(b)
39
+ }
40
+
41
+ // WCAG contrast ratio between two [r,g,b] colors → 1..21.
42
+ export function contrastRatio(rgb1, rgb2) {
43
+ const l1 = relativeLuminance(rgb1)
44
+ const l2 = relativeLuminance(rgb2)
45
+ const [hi, lo] = l1 >= l2 ? [l1, l2] : [l2, l1]
46
+ return (hi + 0.05) / (lo + 0.05)
47
+ }
48
+
49
+ // Does fg-on-bg meet the WCAG threshold? large text & non-text UI components use
50
+ // 3:1; normal text uses 4.5:1 (AA). Returns the ratio, threshold, and pass.
51
+ export function meetsContrast(fg, bg, { large = false, nonText = false } = {}) {
52
+ const ratio = contrastRatio(fg, bg)
53
+ const threshold = nonText || large ? 3 : 4.5
54
+ return { ratio: Math.round(ratio * 100) / 100, threshold, pass: ratio >= threshold }
55
+ }
56
+
57
+ // ---------- pure axe-result classifier (testable, no browser) ----------
58
+
59
+ // Map an axe-core run to one of three Presentation outcomes for color-contrast.
60
+ // `pass` → satisfied; `fail` → hard violation; `abstain` → axe could not decide
61
+ // (`Unverified` until read and marked verified, or escalated).
62
+ export function classifyAxeContrast(axeResults = {}) {
63
+ const pick = (arr) => (arr || []).filter((r) => r.id === 'color-contrast')
64
+ const violations = pick(axeResults.violations)
65
+ const incomplete = pick(axeResults.incomplete)
66
+ if (violations.length) {
67
+ const nodes = violations.flatMap((v) => v.nodes || [])
68
+ return { status: 'fail', count: nodes.length, nodes }
69
+ }
70
+ if (incomplete.length) {
71
+ const nodes = incomplete.flatMap((v) => v.nodes || [])
72
+ return { status: 'abstain', count: nodes.length, nodes }
73
+ }
74
+ return { status: 'pass', count: 0, nodes: [] }
75
+ }
76
+
77
+ // ---------- pure visibility predicate (testable, no browser) ----------
78
+
79
+ // Is an element effectively hidden from a sighted user? Used by the negative form
80
+ // (a privilege-leaked control must be truly gone, not just visually small) and to
81
+ // AVOID false-flagging intentionally non-rendered state. `style` is the element's
82
+ // computed style snapshot; `rect` its bounding box.
83
+ export function isEffectivelyHidden({ style = {}, rect = {}, ariaHidden = false } = {}) {
84
+ if (style.display === 'none') return true
85
+ if (style.visibility === 'hidden' || style.visibility === 'collapse') return true
86
+ if (Number(style.opacity) === 0) return true
87
+ if (ariaHidden) return true
88
+ if ((rect.width || 0) === 0 || (rect.height || 0) === 0) return true
89
+ return false
90
+ }
91
+
92
+ // State-aware: a disabled control is *intended* to be low-contrast (WCAG exempts
93
+ // it), so a contrast check must skip it rather than false-flag.
94
+ //
95
+ // NOTE on where this runs: `assertContrast` delegates to axe-core, which already
96
+ // skips disabled controls natively — so the primary path does NOT call this. This
97
+ // predicate (and `assessElementContrast` below) is the **manual / escalation path**:
98
+ // when axe abstains and a reviewer escalates to per-element checking, they compute
99
+ // contrast for the specific element here, and this is where the disabled-exemption
100
+ // is enforced.
101
+ export function isExemptFromContrast({ disabled = false, ariaDisabled = false } = {}) {
102
+ return disabled || ariaDisabled
103
+ }
104
+
105
+ // Manual / escalation per-element contrast assessment. Composes the disabled
106
+ // exemption with the WCAG threshold check → 'exempt' | 'pass' | 'fail'. Pure and
107
+ // testable; used when axe abstains and a reviewer resolves a specific element by
108
+ // reading its computed fg/bg colors.
109
+ export function assessElementContrast(fg, bg, { large = false, nonText = false, disabled = false, ariaDisabled = false } = {}) {
110
+ if (isExemptFromContrast({ disabled, ariaDisabled })) return { status: 'exempt' }
111
+ const m = meetsContrast(fg, bg, { large, nonText })
112
+ return { status: m.pass ? 'pass' : 'fail', ratio: m.ratio, threshold: m.threshold }
113
+ }
114
+
115
+ // ---------- Playwright wrappers (need a real browser) ----------
116
+
117
+ // Assert the rendered page has no color-contrast violations. Requires
118
+ // @axe-core/playwright (peer dep). On a hard violation → throws Error; on an axe
119
+ // abstain ("incomplete") → throws AbstainError (blocking, resolve by attest/escalate).
120
+ export async function assertContrast(page, { axeBuilder, include, exclude } = {}) {
121
+ if (!axeBuilder) {
122
+ // Lazy import so the pure exports above are usable without the peer dep.
123
+ const mod = await import('@axe-core/playwright')
124
+ const AxeBuilder = mod.default || mod.AxeBuilder
125
+ axeBuilder = new AxeBuilder({ page })
126
+ }
127
+ let builder = axeBuilder.withRules(['color-contrast'])
128
+ if (include) builder = builder.include(include)
129
+ if (exclude) builder = builder.exclude(exclude)
130
+ const results = await builder.analyze()
131
+ const c = classifyAxeContrast(results)
132
+ if (c.status === 'fail') {
133
+ const where = c.nodes.map((n) => ' ' + (n.target || []).join(' ')).join('\n')
134
+ throw new Error(`presentation:perceivability — ${c.count} contrast violation(s):\n${where}`)
135
+ }
136
+ if (c.status === 'abstain') {
137
+ const where = c.nodes.map((n) => ' ' + (n.target || []).join(' ')).join('\n')
138
+ throw new AbstainError(
139
+ `presentation:perceivability — axe could not decide contrast for ${c.count} element(s) ` +
140
+ `(text over image/gradient/obscured). Unverified until checked by hand and marked verified, or escalated ` +
141
+ `to a screenshot/AI-vision triage:\n${where}`,
142
+ c,
143
+ )
144
+ }
145
+ return c // { status: 'pass' }
146
+ }
147
+
148
+ // Negative form: a control gated to a role must NOT be present/visible for a user
149
+ // lacking that role (a visual privilege leak). `locator` should resolve to the
150
+ // gated control; asserts it is absent or effectively hidden. Uses the pure
151
+ // `isEffectivelyHidden` predicate against the element's computed style + box, which
152
+ // catches cases Playwright's `isVisible()` misses (e.g. opacity:0 with non-zero
153
+ // size — visually invisible but still a leak if it were the positive case).
154
+ export async function assertHiddenFromWrongRole(page, locator) {
155
+ const count = await locator.count()
156
+ if (count === 0) return // not rendered — good
157
+ const snap = await locator.first().evaluate((el) => {
158
+ const cs = getComputedStyle(el)
159
+ const r = el.getBoundingClientRect()
160
+ return {
161
+ style: { display: cs.display, visibility: cs.visibility, opacity: cs.opacity },
162
+ rect: { width: r.width, height: r.height },
163
+ ariaHidden: el.getAttribute('aria-hidden') === 'true',
164
+ }
165
+ })
166
+ if (!isEffectivelyHidden(snap)) {
167
+ throw new Error(`presentation:perceivability (negative) — privilege-gated control is visible to a user without the role: ${locator}`)
168
+ }
169
+ }
170
+
171
+ // Negative form: after a delete/optimistic mutation, the item must be gone from the
172
+ // rendered page (no ghost/stale row).
173
+ export async function assertRemovedAfterMutation(page, locator, { timeout = 5000 } = {}) {
174
+ await locator.waitFor({ state: 'detached', timeout }).catch(() => {})
175
+ const count = await locator.count()
176
+ if (count !== 0) {
177
+ throw new Error(`presentation:perceivability (negative) — item still rendered after its mutation (stale/ghost): ${locator}`)
178
+ }
179
+ }