@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,147 @@
1
+ // Presentation route SWEEP — the runner that opens every manifest route in a real
2
+ // browser and asserts the presentation floor, spine first. This is the evidence run that
3
+ // catches a crashing route a green functional E2E suite sails past, because it
4
+ // drives the REAL render path (no first-party interception) over the WHOLE manifest
5
+ // instead of the handful of routes someone wrote a spec for.
6
+ //
7
+ // Implements the result shape in references/presentation-sweep.md. It stays separate
8
+ // from the functional/journey suite and is not a CI gate.
9
+ //
10
+ // Usage (reference; adapt the fixtures to the repo):
11
+ // E2E_AUTH_BYPASS=1 node presentation-sweep.mjs \
12
+ // --manifest e2e/route-manifest.json --base-url http://127.0.0.1:3000 \
13
+ // --out docs/tests/_generated/presentation-results.json
14
+ //
15
+ // Fixtures the repo supplies (see the contract): auth bypass, seeded data, and a
16
+ // param resolver for dynamic segments. Routes that cannot be rendered are reported
17
+ // `blocked`, never silently skipped or passed.
18
+
19
+ import fs from 'node:fs'
20
+ import path from 'node:path'
21
+ import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
22
+
23
+ // ---- pure, unit-testable core (no browser) ----
24
+
25
+ // Expand a manifest into the concrete routes to visit. Dynamic segments ([id], :id)
26
+ // are resolved via `paramResolver(route) -> concreteUrl | null`. A dynamic route
27
+ // with no resolver answer becomes a `blocked` plan entry (cannot render → not pass).
28
+ export function planRoutes(manifest, { paramResolver = () => null, ignore = [] } = {}) {
29
+ const routes = (manifest?.routes || manifest?.surfaces || [])
30
+ .map((r) => (typeof r === 'string' ? { route: r } : r))
31
+ .filter((r) => r.route && (r.kind ? r.kind === 'route' : true))
32
+ .filter((r) => !ignore.some((ig) => r.route.includes(ig)))
33
+ const plan = []
34
+ for (const r of routes) {
35
+ const isDynamic = /[:\[]/.test(r.route)
36
+ if (!isDynamic) { plan.push({ route: r.route, url: r.route }); continue }
37
+ const concrete = paramResolver(r.route)
38
+ if (concrete) plan.push({ route: r.route, url: concrete })
39
+ else plan.push({ route: r.route, url: null, blocked: 'dynamic segment has no param resolver answer' })
40
+ }
41
+ return plan
42
+ }
43
+
44
+ // Reduce raw per-route outcomes into the contract's result rows + an exit verdict.
45
+ // `fail` and `blocked` fail the gate (exit non-zero); `pass` does not.
46
+ export function summarize(rawResults) {
47
+ const results = rawResults.flatMap((r) => r.levels.map((l) => ({
48
+ route: r.route, level: l.level, status: l.status, evidence: l.evidence || undefined,
49
+ })))
50
+ const failed = results.filter((r) => r.status === 'fail' || r.status === 'blocked')
51
+ return { viewportFloor: [375, 1280], results, ok: failed.length === 0, failedCount: failed.length }
52
+ }
53
+
54
+ function parseArgs(argv) {
55
+ const f = {}
56
+ for (let i = 2; i < argv.length; i++) {
57
+ const a = argv[i]
58
+ if (a.startsWith('--')) f[a.slice(2)] = argv[i + 1]?.startsWith('--') || i + 1 >= argv.length ? true : argv[++i]
59
+ }
60
+ return f
61
+ }
62
+
63
+ // ---- browser orchestration (Playwright, lazy-imported) ----
64
+
65
+ // Run the sweep. `fixtures` lets the repo inject auth + param resolution:
66
+ // { applyAuth: async (context) => {}, paramResolver: (route) => url|null,
67
+ // extraFatal: [], ignoreRoutes: [], spatialIgnore: [] }
68
+ export async function runSweep({ manifestPath, baseURL, outPath, fixtures = {} }) {
69
+ const { chromium } = await import('@playwright/test')
70
+ const { watchRenderHealth, isFatalResponse } = await import('./presentation-render-health.mjs')
71
+ const { assertNoHorizontalOverflow } = await import('./presentation-checks.mjs')
72
+
73
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
74
+ const plan = planRoutes(manifest, { paramResolver: fixtures.paramResolver, ignore: fixtures.ignoreRoutes })
75
+
76
+ const browser = await chromium.launch()
77
+ const context = await browser.newContext()
78
+ if (fixtures.applyAuth) await fixtures.applyAuth(context)
79
+
80
+ const raw = []
81
+ for (const entry of plan) {
82
+ if (entry.blocked) {
83
+ raw.push({ route: entry.route, levels: [
84
+ { level: 'presentation:render-health', status: 'blocked', evidence: entry.blocked },
85
+ { level: 'presentation:spatial', status: 'blocked', evidence: entry.blocked },
86
+ ] })
87
+ continue
88
+ }
89
+ const page = await context.newPage()
90
+ const levels = []
91
+ const health = watchRenderHealth(page, { origin: baseURL, extraFatal: fixtures.extraFatal })
92
+ try {
93
+ const res = await page.goto(baseURL + entry.url, { waitUntil: 'domcontentloaded' })
94
+ await page.waitForLoadState('networkidle').catch(() => {})
95
+ // spine: render-health
96
+ const docFatal = res && isFatalResponse(res.status(), res.url(), { origin: baseURL })
97
+ const verdict = await health.evaluate()
98
+ if (docFatal || !verdict.healthy) {
99
+ const ev = docFatal ? `document ${res.status()}` : verdict.offenders.map((o) => `[${o.kind}] ${o.detail}`).join('; ')
100
+ levels.push({ level: 'presentation:render-health', status: 'fail', evidence: ev })
101
+ // layout is not assessable once the page failed to render
102
+ levels.push({ level: 'presentation:spatial', status: 'blocked', evidence: 'render-health failed; layout not assessable' })
103
+ } else {
104
+ levels.push({ level: 'presentation:render-health', status: 'pass' })
105
+ try {
106
+ await assertNoHorizontalOverflow(page, { ignore: fixtures.spatialIgnore || [] })
107
+ levels.push({ level: 'presentation:spatial', status: 'pass' })
108
+ } catch (e) {
109
+ levels.push({ level: 'presentation:spatial', status: 'fail', evidence: e.message })
110
+ }
111
+ }
112
+ } catch (e) {
113
+ levels.push({ level: 'presentation:render-health', status: 'fail', evidence: e.message })
114
+ } finally {
115
+ await page.close()
116
+ }
117
+ raw.push({ route: entry.route, levels })
118
+ }
119
+ await browser.close()
120
+
121
+ const summary = summarize(raw)
122
+ if (outPath) {
123
+ fs.mkdirSync(path.dirname(outPath), { recursive: true })
124
+ fs.writeFileSync(outPath, JSON.stringify(summary, null, 2))
125
+ }
126
+ return summary
127
+ }
128
+
129
+ // CLI entry: load a repo-supplied fixtures module via --fixtures (optional).
130
+ async function main() {
131
+ const f = parseArgs(process.argv)
132
+ const manifestPath = f.manifest || 'e2e/route-manifest.json'
133
+ const baseURL = (f['base-url'] || 'http://127.0.0.1:3000').replace(/\/$/, '')
134
+ const outPath = f.out || 'docs/tests/_generated/presentation-results.json'
135
+ let fixtures = {}
136
+ if (f.fixtures) fixtures = (await import(path.resolve(f.fixtures))).default || {}
137
+ const summary = await runSweep({ manifestPath, baseURL, outPath, fixtures })
138
+ const failed = summary.results.filter((r) => r.status === 'fail' || r.status === 'blocked')
139
+ for (const r of failed) console.error(`✖ ${r.route} ${r.level} — ${r.status}: ${r.evidence || ''}`)
140
+ console.log(`presentation sweep: ${summary.results.length} checks, ${summary.failedCount} failing across ${summary.results.length ? new Set(summary.results.map((r) => r.route)).size : 0} routes`)
141
+ process.exit(summary.ok ? 0 : 1)
142
+ }
143
+
144
+ // Run as CLI only when invoked directly (not when imported by tests).
145
+ if (isMainModule(import.meta.url)) {
146
+ main().catch((e) => { console.error(e); process.exit(1) })
147
+ }
@@ -0,0 +1,71 @@
1
+ // Unit tests for the sweep's pure core (planning + summarize) — no browser.
2
+ import { test } from 'node:test'
3
+ import assert from 'node:assert/strict'
4
+
5
+ import { planRoutes, summarize } from './presentation-sweep.mjs'
6
+
7
+ test('static routes plan to themselves; route-kind filter applies', () => {
8
+ const plan = planRoutes({ surfaces: [
9
+ { route: '/tasks', kind: 'route' },
10
+ { route: '/api/tasks/list', kind: 'api' }, // not a browser route → excluded
11
+ { route: '/dashboard', kind: 'route' },
12
+ ] })
13
+ assert.deepEqual(plan, [
14
+ { route: '/tasks', url: '/tasks' },
15
+ { route: '/dashboard', url: '/dashboard' },
16
+ ])
17
+ })
18
+
19
+ test('dynamic route with a resolver becomes a concrete url', () => {
20
+ const plan = planRoutes(
21
+ { routes: [{ route: '/tasks/[taskRunId]', kind: 'route' }] },
22
+ { paramResolver: (r) => (r.includes('taskRunId') ? '/tasks/tr_seeded_1' : null) },
23
+ )
24
+ assert.deepEqual(plan, [{ route: '/tasks/[taskRunId]', url: '/tasks/tr_seeded_1' }])
25
+ })
26
+
27
+ test('dynamic route with NO resolver answer is blocked, not skipped or passed', () => {
28
+ const plan = planRoutes({ routes: [{ route: '/tasks/[id]', kind: 'route' }] })
29
+ assert.equal(plan.length, 1)
30
+ assert.equal(plan[0].url, null)
31
+ assert.match(plan[0].blocked, /param resolver/)
32
+ })
33
+
34
+ test('ignore list drops routes', () => {
35
+ const plan = planRoutes(
36
+ { routes: [{ route: '/tasks', kind: 'route' }, { route: '/_debug/x', kind: 'route' }] },
37
+ { ignore: ['/_debug'] },
38
+ )
39
+ assert.deepEqual(plan.map((p) => p.route), ['/tasks'])
40
+ })
41
+
42
+ test('accepts a bare string manifest entry', () => {
43
+ const plan = planRoutes({ routes: ['/a', '/b'] })
44
+ assert.deepEqual(plan.map((p) => p.url), ['/a', '/b'])
45
+ })
46
+
47
+ test('summarize: a render-health fail flips the gate to non-ok', () => {
48
+ const s = summarize([
49
+ { route: '/dashboard', levels: [{ level: 'presentation:render-health', status: 'pass' }] },
50
+ { route: '/tasks', levels: [
51
+ { level: 'presentation:render-health', status: 'fail', evidence: "pageerror: reading 'id'" },
52
+ { level: 'presentation:spatial', status: 'blocked', evidence: 'not assessable' },
53
+ ] },
54
+ ])
55
+ assert.equal(s.ok, false)
56
+ assert.equal(s.failedCount, 2) // the fail + the blocked
57
+ assert.deepEqual(s.viewportFloor, [375, 1280])
58
+ const tasksHealth = s.results.find((r) => r.route === '/tasks' && r.level === 'presentation:render-health')
59
+ assert.equal(tasksHealth.status, 'fail')
60
+ })
61
+
62
+ test('summarize: all-pass is ok', () => {
63
+ const s = summarize([
64
+ { route: '/dashboard', levels: [
65
+ { level: 'presentation:render-health', status: 'pass' },
66
+ { level: 'presentation:spatial', status: 'pass' },
67
+ ] },
68
+ ])
69
+ assert.equal(s.ok, true)
70
+ assert.equal(s.failedCount, 0)
71
+ })
@@ -0,0 +1,127 @@
1
+ // Presentation-axis Temporal-stability instrument for e2e-test (issue #199).
2
+ //
3
+ // "Does the layout hold still" — content must not jump as things inject. Tag specs
4
+ // `@level presentation:temporal`. Drives a REAL browser; DOM-only is Over-mocked.
5
+ //
6
+ // Split by WHO renders the shift (the functional axis says "fake externals", but
7
+ // faking the payment iframe makes the shift we want to catch disappear):
8
+ //
9
+ // • FIRST-PARTY shift (lazy images, async data, our own dynamic fields) — a
10
+ // HERMETIC gate: real render, no network. Gate on a stable condition, not a
11
+ // fixed sleep.
12
+ // • THIRD-PARTY iframe shift (Stripe/Adyen/Braintree payfields) — the provider's
13
+ // official SANDBOX iframe in a SEPARATE NON-BLOCKING PR lane: a failure posts a
14
+ // visible annotation and the surface stays `Unverified` until a reviewer
15
+ // attests — never a silent advisory. (Faking the iframe defeats the check.)
16
+ //
17
+ // Why not top-page CLS: the Layout Instability API does NOT report shifts inside
18
+ // cross-origin iframes (WICG spec excludes descendant browsing contexts). So a
19
+ // top-page PerformanceObserver is blind to the payfields case — the deterministic
20
+ // answer is a before/after getBoundingClientRect assertion gated on a provider-ready
21
+ // signal. CLS IS fine for first-party shift.
22
+
23
+ // ---------- pure cores (no browser) ----------
24
+
25
+ // Compare two geometry snapshots and return elements that MOVED beyond tolerance.
26
+ // `before`/`after` are arrays of { selector, rect:{left,top,right,bottom} }; matched
27
+ // by selector. An element only present after (newly injected) is not itself a shift —
28
+ // what matters is whether EXISTING elements moved because of the injection.
29
+ export function findLayoutShift(before, after, { tolerancePx = 2 } = {}) {
30
+ const afterBySel = new Map(after.map((e) => [e.selector, e.rect]))
31
+ const shifted = []
32
+ for (const b of before) {
33
+ const a = afterBySel.get(b.selector)
34
+ if (!a) continue // element gone — not a shift of a stable element
35
+ const dx = Math.max(Math.abs(a.left - b.rect.left), Math.abs(a.right - b.rect.right))
36
+ const dy = Math.max(Math.abs(a.top - b.rect.top), Math.abs(a.bottom - b.rect.bottom))
37
+ if (dx > tolerancePx || dy > tolerancePx) {
38
+ shifted.push({ selector: b.selector, dx: Math.round(dx), dy: Math.round(dy) })
39
+ }
40
+ }
41
+ return shifted
42
+ }
43
+
44
+ // Cumulative Layout Shift from PerformanceObserver `layout-shift` entries: sum of
45
+ // `value` for entries NOT following recent user input. Pure; feed it the raw entries
46
+ // captured in-page. An OPTIONAL first-party alternative to the geometry diff — the
47
+ // wrappers below use `findLayoutShift` (before/after geometry) as the default
48
+ // mechanism because it pinpoints the offending element; CLS is offered for teams
49
+ // that prefer the Web Vital. (First-party only — see header note on the iframe blind spot.)
50
+ export function cumulativeShiftScore(entries = []) {
51
+ return entries
52
+ .filter((e) => !e.hadRecentInput)
53
+ .reduce((sum, e) => sum + (e.value || 0), 0)
54
+ }
55
+
56
+ // ---------- in-browser collectors ----------
57
+
58
+ // NOTE: elements are keyed by a derived selector (`#id` else `tag.class…`). Two
59
+ // id-less elements sharing tag+classes collide on the same key (last-wins), which
60
+ // can mask or spoof a shift. Prefer stable ids / `data-testid` on elements whose
61
+ // stability you assert; id'd elements are unaffected.
62
+ function collectGeometryAll() {
63
+ const sel = (el) => {
64
+ if (el.id) return '#' + el.id
65
+ let s = el.tagName.toLowerCase()
66
+ if (typeof el.className === 'string' && el.className.trim()) s += '.' + el.className.trim().split(/\s+/).join('.')
67
+ return s
68
+ }
69
+ return [...document.querySelectorAll('body *')].map((el) => {
70
+ const r = el.getBoundingClientRect()
71
+ return { selector: sel(el), rect: { left: r.left, top: r.top, right: r.right, bottom: r.bottom } }
72
+ })
73
+ }
74
+
75
+ // ---------- Playwright wrappers (need a real browser) ----------
76
+
77
+ // FIRST-PARTY hermetic gate. Snapshot geometry, run `trigger` (the action that
78
+ // injects first-party content — load more, resolve async data), wait for a STABLE
79
+ // condition (a settle locator becoming visible, else network-idle — never a fixed
80
+ // sleep), snapshot again, and fail if existing elements moved beyond tolerance.
81
+ export async function assertNoFirstPartyShift(page, { trigger, settle, tolerancePx = 2 } = {}) {
82
+ const before = await page.evaluate(collectGeometryAll)
83
+ if (trigger) await trigger()
84
+ if (settle) await settle.waitFor({ state: 'visible' })
85
+ else await page.waitForLoadState('networkidle').catch(() => {})
86
+ const after = await page.evaluate(collectGeometryAll)
87
+ const shifted = findLayoutShift(before, after, { tolerancePx })
88
+ if (shifted.length) {
89
+ const lines = shifted.map((s) => ` ${s.selector} moved Δx=${s.dx} Δy=${s.dy}px`)
90
+ throw new Error(`presentation:temporal (first-party) — layout shifted after injection:\n${lines.join('\n')}`)
91
+ }
92
+ }
93
+
94
+ // THIRD-PARTY (payfields) check. Runs against the provider's SANDBOX iframe. Wait on
95
+ // a provider-ready signal (`ready`: a locator or async fn — NOT a sleep), then assert
96
+ // the elements AROUND the payment container did not move. Intended for the separate
97
+ // NON-BLOCKING lane; a failure here should be annotated and tracked as `Unverified`,
98
+ // not block the PR (it touches an external sandbox).
99
+ export async function assertNoThirdPartyShift(page, { container, ready, tolerancePx = 2 } = {}) {
100
+ const before = await page.evaluate(collectGeometryAll)
101
+ if (typeof ready === 'function') await ready()
102
+ else if (ready) await ready.waitFor({ state: 'visible' })
103
+ const after = await page.evaluate(collectGeometryAll)
104
+ const shifted = findLayoutShift(before, after, { tolerancePx })
105
+ .filter((s) => !container || !s.selector.includes(container)) // the iframe box itself may grow; we care about NEIGHBORS moving
106
+ if (shifted.length) {
107
+ const lines = shifted.map((s) => ` ${s.selector} moved Δx=${s.dx} Δy=${s.dy}px`)
108
+ throw new Error(`presentation:temporal (third-party payfields) — surrounding layout shifted when the provider iframe injected:\n${lines.join('\n')}`)
109
+ }
110
+ }
111
+
112
+ // Mobile dynamic-viewport (dvh) gate. Emulate a mobile viewport, then shrink the
113
+ // height (the mobile address bar collapsing/expanding) and assert content does not
114
+ // jump — catches `100vh`-behind-the-chrome bugs. Gated via emulation with a tuned
115
+ // tolerance; fall back to staging if flake proves unmanageable (see design doc).
116
+ export async function assertNoDynamicViewportShift(page, { width = 390, tall = 844, short = 740, tolerancePx = 2 } = {}) {
117
+ await page.setViewportSize({ width, height: tall })
118
+ await page.waitForLoadState('networkidle').catch(() => {})
119
+ const before = await page.evaluate(collectGeometryAll)
120
+ await page.setViewportSize({ width, height: short }) // address bar appears
121
+ const after = await page.evaluate(collectGeometryAll)
122
+ const shifted = findLayoutShift(before, after, { tolerancePx })
123
+ if (shifted.length) {
124
+ const lines = shifted.map((s) => ` ${s.selector} moved Δx=${s.dx} Δy=${s.dy}px`)
125
+ throw new Error(`presentation:temporal (dynamic viewport) — content jumped when the mobile viewport height changed (100vh/dvh):\n${lines.join('\n')}`)
126
+ }
127
+ }
@@ -0,0 +1,59 @@
1
+ // Unit tests for the pure Temporal-stability cores (issue #199). Run: `node --test`.
2
+ // The Playwright wrappers (assertNoFirstPartyShift / assertNoThirdPartyShift /
3
+ // assertNoDynamicViewportShift) need a real browser and are exercised downstream.
4
+
5
+ import { test } from 'node:test'
6
+ import assert from 'node:assert/strict'
7
+ import { findLayoutShift, cumulativeShiftScore } from './presentation-temporal.mjs'
8
+
9
+ const r = (left, top, right, bottom) => ({ rect: { left, top, right, bottom } })
10
+ const at = (selector, left, top, right, bottom) => ({ selector, ...r(left, top, right, bottom) })
11
+
12
+ // ---------- findLayoutShift ----------
13
+
14
+ test('no shift when geometry is unchanged', () => {
15
+ const snap = [at('.header', 0, 0, 375, 60), at('.cta', 0, 100, 375, 140)]
16
+ assert.deepEqual(findLayoutShift(snap, snap), [])
17
+ })
18
+
19
+ test('flags an element pushed down by injected content', () => {
20
+ const before = [at('.cta', 0, 100, 375, 140)]
21
+ const after = [at('.cta', 0, 260, 375, 300)] // shoved down 160px (a banner injected above)
22
+ const shifted = findLayoutShift(before, after)
23
+ assert.equal(shifted.length, 1)
24
+ assert.equal(shifted[0].selector, '.cta')
25
+ assert.equal(shifted[0].dy, 160)
26
+ })
27
+
28
+ test('sub-tolerance movement is not a shift', () => {
29
+ const before = [at('.x', 0, 100, 375, 140)]
30
+ const after = [at('.x', 0, 101, 375, 141)] // 1px < default 2px tolerance
31
+ assert.deepEqual(findLayoutShift(before, after), [])
32
+ })
33
+
34
+ test('a newly-injected element is not itself counted as a shift', () => {
35
+ const before = [at('.cta', 0, 100, 375, 140)]
36
+ const after = [at('.cta', 0, 100, 375, 140), at('.banner', 0, 0, 375, 80)] // banner is new, cta did not move
37
+ assert.deepEqual(findLayoutShift(before, after), [])
38
+ })
39
+
40
+ test('an element that disappears is not a shift of a stable element', () => {
41
+ const before = [at('.spinner', 0, 0, 50, 50), at('.cta', 0, 100, 375, 140)]
42
+ const after = [at('.cta', 0, 100, 375, 140)] // spinner gone, cta stable
43
+ assert.deepEqual(findLayoutShift(before, after), [])
44
+ })
45
+
46
+ // ---------- cumulativeShiftScore ----------
47
+
48
+ test('cumulativeShiftScore sums values without recent input', () => {
49
+ const entries = [
50
+ { value: 0.05, hadRecentInput: false },
51
+ { value: 0.10, hadRecentInput: false },
52
+ { value: 0.30, hadRecentInput: true }, // user-initiated — excluded
53
+ ]
54
+ assert.ok(Math.abs(cumulativeShiftScore(entries) - 0.15) < 1e-9)
55
+ })
56
+
57
+ test('cumulativeShiftScore is 0 for no entries', () => {
58
+ assert.equal(cumulativeShiftScore([]), 0)
59
+ })
@@ -0,0 +1,82 @@
1
+ // Project the E2E route-manifest FROM the canonical surface inventory + a curated
2
+ // overlay. The manifest is GENERATED, never hand-enumerated — so its route set
3
+ // cannot drift from the inventory (regenerate and compare during the audit).
4
+ //
5
+ // node project-route-manifest.mjs \
6
+ // --inventory docs/tests/_generated/surface-inventory.json \
7
+ // --overlay e2e/route-manifest.overlay.json \
8
+ // --out e2e/route-manifest.json
9
+ //
10
+ // What lives where:
11
+ // - inventory (ensure-coverage, generated) = the route SET + (kind, class).
12
+ // - overlay (curated, small) = E2E-only deviations: tier, ignore
13
+ // (don't sweep), spatial allowlist.
14
+ // - manifest (generated here) = inventory(routes) ⨝ overlay.
15
+ // Coverage is NOT tracked here — the ledger computes it from `@covers` annotations.
16
+ // `specs` is optional/informational only; it is not a coverage signal.
17
+
18
+ import fs from 'node:fs'
19
+ import path from 'node:path'
20
+ import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
21
+
22
+ // Default E2E tier from the inventory's mechanical class. Overlay can raise it
23
+ // (e.g. a route on a core journey → tier 3); it cannot be derived from a single
24
+ // route because "journey" is a cross-page property.
25
+ const DEFAULT_TIER = { read: 1, detail: 2, mutation: 2 }
26
+
27
+ // Pure: inventory + overlay -> manifest object. Deterministic + sorted so the diff
28
+ // is stable.
29
+ export function projectManifest(inventory, overlay = {}) {
30
+ const ov = overlay.routes || {}
31
+ const routes = (inventory?.surfaces || [])
32
+ .filter((s) => s.kind === 'route')
33
+ .filter((s) => !ov[s.id]?.ignore) // curated: explicitly not swept (still owes its ledger floor)
34
+ .map((s) => {
35
+ const o = ov[s.id] || {}
36
+ const entry = { path: s.id, tier: o.tier ?? DEFAULT_TIER[s.class] ?? 1 }
37
+ if (o.specs) entry.specs = o.specs // informational only
38
+ return entry
39
+ })
40
+ .sort((a, b) => a.path.localeCompare(b.path))
41
+ const manifest = { version: 1, generated: true, routes }
42
+ if (overlay.spatialIgnore) manifest.spatialIgnore = overlay.spatialIgnore
43
+ return manifest
44
+ }
45
+
46
+ // Report overlay entries that reference a route no longer in the inventory — curated
47
+ // rot the diff alone won't explain. Returned so the CLI can warn (not throw).
48
+ export function staleOverlayEntries(inventory, overlay = {}) {
49
+ const ids = new Set((inventory?.surfaces || []).filter((s) => s.kind === 'route').map((s) => s.id))
50
+ return Object.keys(overlay.routes || {}).filter((id) => !ids.has(id))
51
+ }
52
+
53
+ function arg(flags, name, def) { return flags[name] ?? def }
54
+
55
+ const USAGE = 'usage: project-route-manifest.mjs --inventory <surface-inventory.json> [--overlay e2e/route-manifest.overlay.json] [--out e2e/route-manifest.json]'
56
+
57
+ function main() {
58
+ const argv = process.argv.slice(2)
59
+ if (argv.includes('--help') || argv.includes('-h')) { console.log(USAGE); return }
60
+ const flags = {}
61
+ for (let i = 0; i < argv.length; i++) if (argv[i].startsWith('--')) flags[argv[i].slice(2)] = argv[++i]
62
+ const inventoryPath = arg(flags, 'inventory', 'docs/tests/_generated/surface-inventory.json')
63
+ const overlayPath = arg(flags, 'overlay', 'e2e/route-manifest.overlay.json')
64
+ const outPath = arg(flags, 'out', 'e2e/route-manifest.json')
65
+
66
+ if (!fs.existsSync(inventoryPath)) {
67
+ console.error(`project-route-manifest: inventory not found: ${inventoryPath} — run your inventory adapter first\n${USAGE}`)
68
+ process.exit(1)
69
+ }
70
+ const inventory = JSON.parse(fs.readFileSync(inventoryPath, 'utf8'))
71
+ const overlay = fs.existsSync(overlayPath) ? JSON.parse(fs.readFileSync(overlayPath, 'utf8')) : {}
72
+
73
+ const stale = staleOverlayEntries(inventory, overlay)
74
+ if (stale.length) console.error(`⚠ overlay references routes not in inventory (stale curation): ${stale.join(', ')}`)
75
+
76
+ const manifest = projectManifest(inventory, overlay)
77
+ fs.mkdirSync(path.dirname(outPath), { recursive: true })
78
+ fs.writeFileSync(outPath, JSON.stringify(manifest, null, 2) + '\n')
79
+ console.log(`wrote ${outPath} — ${manifest.routes.length} routes (generated from ${inventoryPath})`)
80
+ }
81
+
82
+ if (isMainModule(import.meta.url)) main()
@@ -0,0 +1,76 @@
1
+ import { test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import { projectManifest, staleOverlayEntries } from './project-route-manifest.mjs'
5
+
6
+ const INVENTORY = {
7
+ surfaces: [
8
+ { id: '/tasks', kind: 'route', class: 'read', reachability: 'reachable' },
9
+ { id: '/tasks/[id]', kind: 'route', class: 'detail', reachability: 'reachable' },
10
+ { id: '/settings/models', kind: 'route', class: 'mutation', reachability: 'reachable' },
11
+ { id: '/legacy', kind: 'route', class: 'read', reachability: 'reachable' },
12
+ { id: '/api/tasks/list', kind: 'api', class: 'read' }, // not a browser route
13
+ { id: 'tasks_table', kind: 'table' },
14
+ ],
15
+ }
16
+
17
+ test('projects only browser routes, default tier from class, sorted', () => {
18
+ const m = projectManifest(INVENTORY)
19
+ assert.equal(m.generated, true)
20
+ assert.deepEqual(m.routes, [
21
+ { path: '/legacy', tier: 1 },
22
+ { path: '/settings/models', tier: 2 },
23
+ { path: '/tasks', tier: 1 },
24
+ { path: '/tasks/[id]', tier: 2 },
25
+ ])
26
+ })
27
+
28
+ test('overlay raises tier and drops ignored routes', () => {
29
+ const overlay = { routes: { '/tasks': { tier: 3 }, '/legacy': { ignore: true } }, spatialIgnore: ['.sticky-header'] }
30
+ const m = projectManifest(INVENTORY, overlay)
31
+ assert.equal(m.routes.find((r) => r.path === '/tasks').tier, 3)
32
+ assert.ok(!m.routes.some((r) => r.path === '/legacy'), 'ignored route dropped from sweep')
33
+ assert.deepEqual(m.spatialIgnore, ['.sticky-header'])
34
+ })
35
+
36
+ test('overlay specs are passed through but optional (informational only)', () => {
37
+ const m = projectManifest(INVENTORY, { routes: { '/tasks': { specs: ['render/tasks.spec.ts'] } } })
38
+ assert.deepEqual(m.routes.find((r) => r.path === '/tasks').specs, ['render/tasks.spec.ts'])
39
+ // a route with no overlay has no specs key
40
+ assert.equal('specs' in m.routes.find((r) => r.path === '/settings/models'), false)
41
+ })
42
+
43
+ test('same inventory + overlay → identical manifest (no drift, stable diff)', () => {
44
+ const a = projectManifest(INVENTORY, { routes: { '/tasks': { tier: 3 } } })
45
+ const b = projectManifest(INVENTORY, { routes: { '/tasks': { tier: 3 } } })
46
+ assert.deepEqual(a, b)
47
+ })
48
+
49
+ test('staleOverlayEntries flags curation for a route no longer in inventory', () => {
50
+ const stale = staleOverlayEntries(INVENTORY, { routes: { '/tasks': { tier: 3 }, '/removed': { ignore: true } } })
51
+ assert.deepEqual(stale, ['/removed'])
52
+ })
53
+
54
+ import { spawnSync } from 'node:child_process'
55
+ import { fileURLToPath } from 'node:url'
56
+ import fs from 'node:fs'
57
+ import os from 'node:os'
58
+ import path from 'node:path'
59
+
60
+ const CLI = fileURLToPath(new URL('./project-route-manifest.mjs', import.meta.url))
61
+
62
+ test('CLI: --help prints usage; a missing inventory is a one-line error, not a stack trace', () => {
63
+ const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'prm-cli-'))
64
+ try {
65
+ const help = spawnSync('node', [CLI, '--help'], { cwd, encoding: 'utf8' })
66
+ assert.equal(help.status, 0)
67
+ assert.match(help.stdout, /usage: project-route-manifest\.mjs/)
68
+ const missing = spawnSync('node', [CLI, '--inventory', path.join(cwd, 'nope.json')], { cwd, encoding: 'utf8' })
69
+ assert.equal(missing.status, 1)
70
+ assert.match(missing.stderr, /inventory not found/)
71
+ assert.doesNotMatch(missing.stderr, /at Object\.|node:internal/, 'no stack trace')
72
+ assert.deepEqual(fs.readdirSync(cwd), [], 'nothing written')
73
+ } finally {
74
+ fs.rmSync(cwd, { recursive: true, force: true })
75
+ }
76
+ })
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Validate route manifest against actual spec files.
5
+ *
6
+ * Usage:
7
+ * node scripts/validate-manifest.mjs [--manifest e2e/route-manifest.json] [--e2e-dir e2e]
8
+ *
9
+ * Checks:
10
+ * 1. Manifest is valid JSON with required fields
11
+ * 2. All referenced spec files exist
12
+ * 3. Reports uncovered routes (specs: [])
13
+ * 4. Reports coverage percentage
14
+ *
15
+ * Exit codes:
16
+ * 0 = valid (may have warnings)
17
+ * 1 = errors found (missing files, invalid format)
18
+ */
19
+
20
+ import { readFileSync, existsSync } from 'fs';
21
+ import { join, resolve } from 'path';
22
+
23
+ const args = process.argv.slice(2);
24
+ function getArg(name, defaultVal) {
25
+ const idx = args.indexOf(`--${name}`);
26
+ return idx >= 0 && args[idx + 1] ? args[idx + 1] : defaultVal;
27
+ }
28
+
29
+ const manifestPath = resolve(getArg('manifest', 'e2e/route-manifest.json'));
30
+ const e2eDir = resolve(getArg('e2e-dir', 'e2e'));
31
+
32
+ let errors = 0;
33
+ let warnings = 0;
34
+
35
+ // Check manifest exists
36
+ if (!existsSync(manifestPath)) {
37
+ console.error(`❌ Manifest not found: ${manifestPath}`);
38
+ process.exit(1);
39
+ }
40
+
41
+ // Parse manifest
42
+ let manifest;
43
+ try {
44
+ manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
45
+ } catch (e) {
46
+ console.error(`❌ Invalid JSON in manifest: ${e.message}`);
47
+ process.exit(1);
48
+ }
49
+
50
+ const routes = manifest.routes || manifest.endpoints || [];
51
+
52
+ if (routes.length === 0) {
53
+ console.error('❌ Manifest has no routes/endpoints');
54
+ process.exit(1);
55
+ }
56
+
57
+ console.log(`📋 Validating ${routes.length} routes from ${manifestPath}\n`);
58
+
59
+ // Validate each route
60
+ let covered = 0;
61
+ for (const route of routes) {
62
+ const label = route.method ? `${route.method} ${route.path}` : route.path;
63
+
64
+ // Check required fields
65
+ if (!route.path) {
66
+ console.error(`❌ Route missing "path" field`);
67
+ errors++;
68
+ continue;
69
+ }
70
+ if (typeof route.tier !== 'number' || route.tier < 1 || route.tier > 3) {
71
+ console.error(`❌ ${label}: invalid tier (must be 1-3, got ${route.tier})`);
72
+ errors++;
73
+ }
74
+
75
+ // Check specs
76
+ const specs = route.specs || [];
77
+ if (specs.length === 0) {
78
+ console.warn(`⚠️ ${label}: uncovered (no specs)`);
79
+ warnings++;
80
+ continue;
81
+ }
82
+
83
+ covered++;
84
+ for (const spec of specs) {
85
+ const specPath = join(e2eDir, spec);
86
+ if (!existsSync(specPath)) {
87
+ console.error(`❌ ${label}: spec file not found: ${specPath}`);
88
+ errors++;
89
+ }
90
+ }
91
+ }
92
+
93
+ // Summary
94
+ const coverage = Math.round((covered / routes.length) * 100);
95
+ console.log(`\n${'─'.repeat(50)}`);
96
+ console.log(`📊 Coverage: ${coverage}% (${covered}/${routes.length} routes covered)`);
97
+ console.log(`❌ Errors: ${errors}`);
98
+ console.log(`⚠️ Warnings: ${warnings}`);
99
+
100
+ if (errors > 0) {
101
+ console.log('\n🚫 Validation failed');
102
+ process.exit(1);
103
+ } else {
104
+ console.log('\n✅ Validation passed');
105
+ process.exit(0);
106
+ }